# HG changeset patch # User paulb@localhost.localdomain # Date 1182625801 -7200 # Node ID 90d94ecbdaf5b4b09a3aae11b9cb079c90a34b20 # Parent 6e7b6fcd63025ed3f1939cc629a8ada4a162bb2e Moved the Importer to the simplify top-level module; removed the load function from the simplify.annotate module. Introduced a different API for loading modules, making direct use of importers and registering the main and builtins modules in those importers. Moved instance fixing into the importer mechanisms. Changed the documentation preparation to use a plain list of modules, along with a filename. Added subprogram replacement by assuming that subprograms belonging to (or created for) equivalent instances are also the same. Renamed _Class to GeneralClass and introduced a get_class function which can be used to create the appropriate kind of classes - this removes the need for "import gymnastics" where the setting of the appropriate system class had to be done before certain imports were performed. diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/__init__.py --- a/simplify/__init__.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/__init__.py Sat Jun 23 21:10:01 2007 +0200 @@ -3,7 +3,7 @@ """ The simplify package for processing Python source code. -Copyright (C) 2007 Paul Boddie +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 @@ -19,6 +19,196 @@ 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, an importer should be constructed and the load method used. +Here, the standard path for module searching is used: + +importer = Importer(sys.path) +importer.load_from_file(builtins) +importer.load_from_file(filename, builtins) """ +from simplify.simplified import * +import simplify.ast +import simplify.fixnames +import simplify.annotate +import simplify.fixinstances + +class Importer: + + "An import machine, searching for and loading modules." + + def __init__(self, path=None, annotate=1, fixinstances=1): + + """ + Initialise the importer with the given search 'path' - a list of + directories to search for Python modules. If the optional 'annotate' + parameter is set to a false value (unlike the default), no annotation + will be performed. + false value (unlike the default), no instance fixing will be performed. + """ + + self.path = path or [os.getcwd()] + self.path.append(libdir) + self.annotate = annotate + self.modules = {} + + def get_modules(self): + return self.modules.values() + + def find_in_path(self, name): + + """ + Find the given module 'name' in the search path, returning None where no + such module could be found, or a 2-tuple from the 'find' method + otherwise. + """ + + for d in self.path: + m = self.find(d, name) + if m: return m + return None + + def find(self, d, name): + + """ + In the directory 'd', find the given module 'name', where 'name' can + either refer to a single file module or to a package. Return None if the + 'name' cannot be associated with either a file or a package directory, + or a 2-tuple from '_find_package' or '_find_module' otherwise. + """ + + m = self._find_package(d, name) + if m: return m + m = self._find_module(d, name) + if m: return m + return None + + def _find_module(self, d, name): + + """ + In the directory 'd', find the given module 'name', returning None where + no suitable file exists in the directory, or a 2-tuple consisting of + None (indicating that no package directory is involved) and a filename + indicating the location of the module. + """ + + name_py = name + os.extsep + "py" + filename = self._find_file(d, name_py) + if filename: + return None, filename + return None + + def _find_package(self, d, name): + + """ + In the directory 'd', find the given package 'name', returning None + where no suitable package directory exists, or a 2-tuple consisting of + a directory (indicating the location of the package directory itself) + and a filename indicating the location of the __init__.py module which + declares the package's top-level contents. + """ + + filename = self._find_file(d, name) + if filename: + init_py = "__init__" + os.path.extsep + "py" + init_py_filename = self._find_file(filename, init_py) + if init_py_filename: + return filename, init_py_filename + return None + + def _find_file(self, d, filename): + + """ + Return the filename obtained when searching the directory 'd' for the + given 'filename', or None if no actual file exists for the filename. + """ + + filename = os.path.join(d, filename) + if os.path.exists(filename): + return filename + else: + return None + + def load(self, name, builtins=None, alias=None): + + """ + Load the module or package with the given 'name' and using the specified + 'builtins'. Return an Attribute object referencing the loaded module or + package, or None if no such module or package exists. + """ + + if self.modules.has_key(name): + return Attribute(None, self.modules[name]) + + path = name.split(".") + m = self.find_in_path(path[0]) + if not m: + return None # NOTE: Import error. + d, filename = m + + if self.modules.has_key(path[0]): + top = module = self.modules[path[0]] + else: + top = module = self.load_from_file(filename, builtins, path[0]) + + if len(path) > 1: + path_so_far = path[:1] + for p in path[1:]: + path_so_far.append(p) + m = self.find(d, p) + if not m: + return None # NOTE: Import error. + d, filename = m + module_name = ".".join(path_so_far) + + if self.modules.has_key(module_name): + submodule = self.modules[module_name] + else: + submodule = self.load_from_file(filename, builtins, module_name) + + # Store the submodule within its parent module. + + module.namespace[p] = [Attribute(None, submodule)] + module = submodule + + if alias: + return Attribute(None, module) + else: + return Attribute(None, top) + + def load_from_file(self, name, builtins=None, module_name=None): + + """ + Load the module with the given 'name' (which may be a full module path), + using the optional 'builtins' to resolve built-in names. + """ + + if module_name is None: + if builtins is None: + module_name = "__builtins__" + else: + module_name = "__main__" + + module = simplify.ast.simplify(name, builtins is None, module_name) + simplify.fixnames.fix(module, builtins) + if self.annotate: + simplify.annotate.annotate(module, builtins, self) + + # Record the module. + + self.modules[module_name] = module + return module + + def fix_instances(self): + + "Fix instances for all modules loaded by this importer." + + for module in self.get_modules(): + simplify.fixinstances.fix_structures(module) + for module in self.get_modules(): + simplify.fixinstances.fix(module) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/annotate.py --- a/simplify/annotate.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/annotate.py Sat Jun 23 21:10:01 2007 +0200 @@ -24,25 +24,17 @@ -------- -To use this module, the easiest approach is to use the load function: - -load(filename, builtins) - -To control module importing, an importer should be constructed and employed. -Here, the standard path for module searching is used: +To use this module, the easiest approach is to use the annotate function which +provides support for annotating modules already processed by simplify and +fixnames: -importer = Importer(sys.path) -load(filename, builtins, importer) +annotate(builtins, None, importer) +annotate(module, builtins, importer) -Underneath the load function, the annotate function provides support for -annotating modules already processed by simplify and fixnames: - -annotate(module, builtins) - -And at the most basic level, the most intricate approach involves obtaining an +At the most basic level, the most intricate approach involves obtaining an Annotator object: -annotator = Annotator() +annotator = Annotator(importer) Then, processing an existing module with it: @@ -55,7 +47,6 @@ """ from simplify.simplified import * -import simplify.ast, simplify.fixnames # for the load function import compiler import os @@ -139,13 +130,13 @@ Try. """ - def __init__(self, importer=None): + def __init__(self, importer): - "Initialise the visitor with an optional 'importer'." + "Initialise the visitor with an 'importer'." Visitor.__init__(self) self.system = system - self.importer = importer or Importer() + self.importer = importer # Satisfy visitor issues. @@ -487,7 +478,7 @@ # exist for some combinations of classes in a hierarchy but not for # others. - if isinstance(attr.type, Class): + if isinstance(attr.type, GeneralClass): attributes = get_attributes(attr.type, "__init__") # Deal with object invocations by using __call__ methods. @@ -509,7 +500,7 @@ # If a class is involved, presume that it must create a new # object. - if isinstance(attr.type, Class): + if isinstance(attr.type, GeneralClass): # Instantiate the class. @@ -534,7 +525,7 @@ if attribute.type not in invocations: invocations.append(attribute.type) - elif not isinstance(attr.type, Class): + elif not isinstance(attr.type, GeneralClass): print "Invocation type is None for", accessor else: @@ -547,7 +538,7 @@ # Special case: initialisation. - if isinstance(attr.type, Class): + if isinstance(attr.type, GeneralClass): # Associate the instance with the result of this invocation. @@ -811,7 +802,7 @@ raise_.dstar = None types = set() for attr in self.namespace.types: - if isinstance(attr.type, Class): + if isinstance(attr.type, GeneralClass): self._visitInvoke(raise_, [attr], have_args=0) types.update(self.namespace.types) else: @@ -1383,144 +1374,6 @@ # Namespace-related abstractions. -class Importer: - - "An import machine, searching for and loading modules." - - def __init__(self, path=None): - - """ - Initialise the importer with the given search 'path' - a list of - directories to search for Python modules. - """ - - self.path = path or [os.getcwd()] - self.path.append(libdir) - self.modules = {} - - def find_in_path(self, name): - - """ - Find the given module 'name' in the search path, returning None where no - such module could be found, or a 2-tuple from the 'find' method - otherwise. - """ - - for d in self.path: - m = self.find(d, name) - if m: return m - return None - - def find(self, d, name): - - """ - In the directory 'd', find the given module 'name', where 'name' can - either refer to a single file module or to a package. Return None if the - 'name' cannot be associated with either a file or a package directory, - or a 2-tuple from '_find_package' or '_find_module' otherwise. - """ - - m = self._find_package(d, name) - if m: return m - m = self._find_module(d, name) - if m: return m - return None - - def _find_module(self, d, name): - - """ - In the directory 'd', find the given module 'name', returning None where - no suitable file exists in the directory, or a 2-tuple consisting of - None (indicating that no package directory is involved) and a filename - indicating the location of the module. - """ - - name_py = name + os.extsep + "py" - filename = self._find_file(d, name_py) - if filename: - return None, filename - return None - - def _find_package(self, d, name): - - """ - In the directory 'd', find the given package 'name', returning None - where no suitable package directory exists, or a 2-tuple consisting of - a directory (indicating the location of the package directory itself) - and a filename indicating the location of the __init__.py module which - declares the package's top-level contents. - """ - - filename = self._find_file(d, name) - if filename: - init_py = "__init__" + os.path.extsep + "py" - init_py_filename = self._find_file(filename, init_py) - if init_py_filename: - return filename, init_py_filename - return None - - def _find_file(self, d, filename): - - """ - Return the filename obtained when searching the directory 'd' for the - given 'filename', or None if no actual file exists for the filename. - """ - - filename = os.path.join(d, filename) - if os.path.exists(filename): - return filename - else: - return None - - def load(self, name, builtins, alias=None): - - """ - Load the module or package with the given 'name' and using the specified - 'builtins'. Return an Attribute object referencing the loaded module or - package, or None if no such module or package exists. - """ - - if self.modules.has_key(name): - return Attribute(None, self.modules[name]) - - path = name.split(".") - m = self.find_in_path(path[0]) - if not m: - return None # NOTE: Import error. - d, filename = m - - if self.modules.has_key(path[0]): - top = module = self.modules[path[0]] - else: - top = module = self.modules[path[0]] = load(filename, builtins, path[0], self, no_annotate=1) - annotate(module, builtins, self) - - if len(path) > 1: - path_so_far = path[:1] - for p in path[1:]: - path_so_far.append(p) - m = self.find(d, p) - if not m: - return None # NOTE: Import error. - d, filename = m - module_name = ".".join(path_so_far) - - if self.modules.has_key(module_name): - submodule = self.modules[module_name] - else: - submodule = self.modules[module_name] = load(filename, builtins, module_name, self, no_annotate=1) - annotate(submodule, builtins, self) - - # Store the submodule within its parent module. - - module.namespace[p] = [Attribute(None, submodule)] - module = submodule - - if alias: - return Attribute(None, module) - else: - return Attribute(None, top) - def combine(target, additions): """ @@ -1566,7 +1419,7 @@ # Investigate any class's base classes. - elif isinstance(structure, Class): + elif isinstance(structure, GeneralClass): # If no base classes exist, return an indicator that no attribute # exists. @@ -1609,7 +1462,7 @@ # Detect class attribute access via instances. - if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, Class): + if attribute is not None and isinstance(structure, Instance) and isinstance(accessor, GeneralClass): attribute = accessor.get_attribute_for_instance(attribute, structure) # Produce an attribute with the appropriate context. @@ -1640,26 +1493,11 @@ # Convenience functions. -def load(name, builtins=None, module_name=None, importer=None, no_annotate=0): +def annotate(module, builtins, importer): """ - Load the module with the given 'name' (which may be a full module path), - using the optional 'builtins' to resolve built-in names, and using the - optional 'importer' to provide a means of finding and loading modules. - """ - - module = simplify.ast.simplify(name, builtins is None, module_name) - simplify.fixnames.fix(module, builtins) - if not no_annotate: - annotate(module, builtins, importer) - return module - -def annotate(module, builtins=None, importer=None): - - """ - Annotate the given 'module', also employing the optional 'builtins' module, - if specified. If the optional 'importer' is given, use that to find and load - modules. + Annotate the given 'module', also employing the 'builtins' module which may + be specified as None. Use the 'importer' to find and load modules. """ annotator = Annotator(importer) diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/ast.py --- a/simplify/ast.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/ast.py Sat Jun 23 21:10:01 2007 +0200 @@ -547,7 +547,7 @@ else: bases = class_.bases - structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases)) + structure = get_class()(name=class_.name, module=self.module, bases=self.dispatches(bases)) self.structures.append(structure) within_class = self.within_class self.within_class = 1 diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/fixinstances.py --- a/simplify/fixinstances.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/fixinstances.py Sat Jun 23 21:10:01 2007 +0200 @@ -174,6 +174,12 @@ return node def _replace(self, items, name=None): + + """ + Produce a new list or set for the given 'items', acquired from the + annotation having the given 'name'. + """ + if name == "accesses": new_items = [] else: @@ -192,9 +198,29 @@ return new_items def _get_replacement(self, value): + + "Get a replacement for the given 'value'." + + # Find the distinct instance for any given instance. + if isinstance(value, Instance): distinct_instances = value.get_class().get_distinct_instances() return distinct_instances[value] + + # For subprograms, find the distinct instance's copy for the owner + # instance; otherwise, return the original subprogram. + + elif isinstance(value, Subprogram): + if hasattr(value, "copy_of") and hasattr(value, "instance"): + cls = value.instance.get_class() + distinct = cls.get_distinct_instances() + instance = distinct[value.instance] + return value.copy_of.copies.get(instance, value) + else: + return value + + # Return all other values as they are. + else: return value diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/simplified/__init__.py --- a/simplify/simplified/__init__.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/simplified/__init__.py Sat Jun 23 21:10:01 2007 +0200 @@ -55,4 +55,7 @@ global Class Class = ProlificMultipleInstanceClass +def get_class(): + return Class + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/simplified/data.py --- a/simplify/simplified/data.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/simplified/data.py Sat Jun 23 21:10:01 2007 +0200 @@ -30,7 +30,7 @@ # Special non-program nodes. -class _Class(Structure, WithName): +class GeneralClass(Structure, WithName): """ The basis of a Python class. Classes with specific instantiation behaviour @@ -117,12 +117,12 @@ return instances -class SingleInstanceClass(_Class): +class SingleInstanceClass(GeneralClass): "A Python class producing only one instance." def __init__(self, *args, **kw): - _Class.__init__(self, *args, **kw) + GeneralClass.__init__(self, *args, **kw) self.instance = None def has_instance(self, node): @@ -148,12 +148,12 @@ def get_attribute_for_instance(self, attribute, instance): return attribute -class MultipleInstanceClass(_Class): +class MultipleInstanceClass(GeneralClass): "A Python class producing many instances." def __init__(self, *args, **kw): - _Class.__init__(self, *args, **kw) + GeneralClass.__init__(self, *args, **kw) self.instances = {} self.attributes_for_instances = {} diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 simplify/viewer.py --- a/simplify/viewer.py Sat Jun 23 01:57:13 2007 +0200 +++ b/simplify/viewer.py Sat Jun 23 21:10:01 2007 +0200 @@ -1302,11 +1302,11 @@ finally: stream.close() -def makedocs(module, modules, builtins, distinct=0): - dirname = "%s-docs" % module.name +def makedocs(filename, modules, distinct=0): + dirname = "%s-docs" % os.path.split(os.path.splitext(filename)[0])[-1] if not os.path.exists(dirname): os.mkdir(dirname) - for m in [module, builtins] + modules: + for m in 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)), distinct=distinct) diff -r 6e7b6fcd6302 -r 90d94ecbdaf5 test.py --- a/test.py Sat Jun 23 01:57:13 2007 +0200 +++ b/test.py Sat Jun 23 21:10:01 2007 +0200 @@ -16,22 +16,14 @@ simplified.set_prolific_multiple_instance_mode() import simplify.viewer - import simplify.fixinstances - from simplify.annotate import AnnotationError, Importer, load + from simplify import Importer - importer = Importer(sys.path) - try: - builtins = load(os.path.join("lib", "builtins.py")) - module = load(sys.argv[1], builtins, None, importer, "-na" in sys.argv) - except simplified.SimplifiedError, exc: - raise - else: - if "-i" in sys.argv: - simplify.fixinstances.fix_structures(module) - simplify.fixinstances.fix_structures(builtins) - simplify.fixinstances.fix(module) - simplify.fixinstances.fix(builtins) - if "-d" in sys.argv: - simplify.viewer.makedocs(module, importer.modules.values(), builtins, distinct=("-i" in sys.argv)) + importer = Importer(sys.path, "-na" not in sys.argv) + builtins = importer.load_from_file(os.path.join("lib", "builtins.py")) + module = importer.load_from_file(sys.argv[1], builtins) + if "-i" in sys.argv: + importer.fix_instances() + if "-d" in sys.argv: + simplify.viewer.makedocs(sys.argv[1], importer.get_modules(), distinct=("-i" in sys.argv)) # vim: tabstop=4 expandtab shiftwidth=4