micropython

micropython/__init__.py

139:29103989ca36
2008-09-05 Paul Boddie Added the final Return instruction to generated images. Fixed the For node processing to store assigned values before visiting assignment nodes. Changed source processing to not use temporary storage optimisations, since the conditions for such optimisations are not met in general when processing assignments. Adopted a list to hold sources (assignment expression values), since list and tuple assignment act on a hierarchy of such values. Added elementary support and tests for list assignment.
     1 #!/usr/bin/env python     2      3 """     4 The micropython package for processing Python source code. The code originates     5 from the simplify package but has had various details related to that package     6 removed.     7      8 Copyright (C) 2006, 2007, 2008 Paul Boddie <paul@boddie.org.uk>     9     10 This program is free software; you can redistribute it and/or modify it under    11 the terms of the GNU General Public License as published by the Free Software    12 Foundation; either version 3 of the License, or (at your option) any later    13 version.    14     15 This program is distributed in the hope that it will be useful, but WITHOUT    16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    17 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    18 details.    19     20 You should have received a copy of the GNU General Public License along with    21 this program.  If not, see <http://www.gnu.org/licenses/>.    22     23 --------    24     25 To use this module, an importer should be constructed and the load_from_file    26 method used. Here, the standard path for module searching is employed:    27     28 importer = Importer(sys.path)    29 importer.load_from_file(filename)    30 importer.vacuum()    31     32 Such importer objects are the most convenient mechanism through which the    33 functionality of the micropython package may be accessed.    34 """    35     36 from micropython.common import *    37 import micropython.ast    38 import micropython.inspect    39 import micropython.table    40 import os    41 try:    42     set    43 except NameError:    44     from sets import Set as set    45     46 class Importer:    47     48     """    49     An import machine, searching for and loading modules. In addition, this    50     class supports the generation of a program image.    51     """    52     53     supported_optimisations = micropython.ast.Translation.supported_optimisations    54     55     def __init__(self, path=None, verbose=0):    56     57         """    58         Initialise the importer with the given search 'path' - a list of    59         directories to search for Python modules.    60     61         The optional 'verbose' parameter causes output concerning the activities    62         of the object to be produced if set to a true value (not the default).    63         """    64     65         self.path = path or [os.getcwd()]    66         self.verbose = verbose    67         self.modules = {}    68         self.modules_ordered = []    69         self.loading = set()    70     71         # Remember the tables once generated.    72     73         self.objtable = None    74         self.paramtable = None    75         self.clstable = None    76     77         # Constant records.    78     79         self.constant_values = {}    80         self.constant_list = None # cache for constants    81     82         # Main program information.    83     84         self.code = None    85         self.code_location = None    86     87     def constants(self):    88     89         "Return a list of constants."    90     91         if self.constant_list is None:    92             self.constant_list = list(self.constant_values.values())    93     94         return self.constant_list    95     96     def vacuum(self):    97     98         "Tidy up the modules."    99    100         for name, module in self.modules.items():   101             if module.loaded:   102                 module.vacuum()   103             else:   104                 del self.modules[name]   105    106     def finalise(self):   107    108         "Finalise the program."   109    110         for module in self.get_modules():   111    112             # Fix the attributes.   113    114             module.finalise_attributes()   115    116             for obj in module.all_objects:   117                 if isinstance(obj, (micropython.inspect.Class, micropython.inspect.Function)):   118                     obj.finalise_attributes()   119    120     def get_modules(self):   121    122         "Return all modules known to the importer."   123    124         return self.modules.values()   125    126     def get_image(self, with_builtins=0, optimisations=None):   127    128         "Return a dictionary mapping modules to structures."   129    130         if self.code is not None:   131             return self.code   132    133         objtable = self.get_object_table()   134         paramtable = self.get_parameter_table()   135         self.finalise()   136    137         image = []   138    139         # Append constants to the image.   140    141         for pos, const in enumerate(self.constants()):   142             const.location = pos   143             image.append(const)   144    145         last_module = self.modules_ordered[-1]   146    147         for module in self.modules_ordered:   148    149             if not with_builtins and module.name == "__builtins__":   150                 continue   151    152             pos = len(image)   153    154             # Position the module in the image and make a translation.   155    156             module.location = pos   157             trans = micropython.ast.Translation(module, self, optimisations)   158    159             # Add header details.   160    161             image.append(module)   162             pos += 1   163    164             # Append module attributes to the image.   165    166             attributes = module.module_attributes()   167             image += module.attributes_as_list()   168             pos += len(attributes.keys())   169    170             # Append classes and functions to the image.   171    172             for obj in module.all_objects:   173                 if isinstance(obj, micropython.inspect.Class):   174    175                     # Position the class in the image.   176    177                     obj.location = pos   178    179                     # Add header details.   180    181                     image.append(obj)   182                     pos += 1   183    184                     # Append class attributes to the image.   185    186                     attributes = obj.class_attributes()   187                     image += obj.attributes_as_list()   188                     pos += len(attributes.keys())   189    190                     # Generate the instantiator/initialiser.   191                     # Append the function code to the image.   192    193                     instantiator = obj.get_instantiator()   194                     instantiator.code_location = pos   195                     code = trans.get_instantiator_code(obj)   196                     image += code   197                     pos += len(code)   198    199                     # Class-level code is generated separately at the module   200                     # level, and the code location is set within the code   201                     # generation process for the module.   202    203                 elif isinstance(obj, micropython.inspect.Function):   204    205                     # Position the function in the image.   206    207                     obj.location = pos   208    209                     # Add header details.   210    211                     image.append(obj)   212                     pos += 1   213    214                     # Append any default values to the image.   215                     # Only do this for named functions (not lambdas).   216    217                     if obj.name is not None:   218                         image += obj.default_attrs   219                         pos += len(obj.default_attrs)   220    221                     # Append the function code to the image.   222    223                     obj.code_location = pos   224                     code = trans.get_code(obj)   225                     image += code   226                     pos += len(code)   227    228             # Remember the position of the module code.   229    230             module.code_location = pos   231    232             # Append the module top-level code to the image.   233    234             code = trans.get_module_code(final=(module is last_module))   235             image += code   236             pos += len(code)   237    238         # Remember the generated code and the location of the first instruction.   239    240         self.code = image   241         self.code_location = self.modules["__main__"].code_location   242         return image   243    244     def get_object_table(self):   245    246         "Return a table with details of attributes for classes and modules."   247    248         if self.objtable is None:   249             t = self.objtable = micropython.table.ObjectTable()   250             for module in self.get_modules():   251                 t.add(module.full_name(), module.module_attributes())   252    253                 # Add class and instance attributes for all classes, together   254                 # with descendant information.   255    256                 for obj in module.all_objects:   257                     if isinstance(obj, micropython.inspect.Class):   258                         attributes = {obj.full_name() : obj}   259                         attributes.update(obj.all_attributes())   260                         attributes.update(obj.all_descendants())   261                         t.add(obj.full_name(), attributes)   262    263         return self.objtable   264    265     def get_parameter_table(self):   266    267         "Return a table with details of parameters for functions and methods."   268    269         # Need the object table to get at class details.   270    271         objtable = self.get_object_table()   272    273         if self.paramtable is None:   274             t = self.paramtable = micropython.table.ParameterTable()   275    276             # Visit each module, getting function and method details.   277    278             for module in self.get_modules():   279                 for obj in module.all_objects:   280                     if isinstance(obj, micropython.inspect.Function):   281                         t.add(obj.full_name(), obj.parameters())   282    283                     # Classes are callable, too.   284                     # Take details of the appropriate __init__ method to make an   285                     # entry for an instantiation function for the class.   286    287                     elif isinstance(obj, micropython.inspect.Class):   288                         t.add(obj.full_name(), obj.get_instantiator().parameters())   289    290             # Filter out all parameter table entries not referenced by keyword   291             # arguments.   292    293             keyword_names = set()   294    295             for module in self.get_modules():   296                 keyword_names.update(module.keyword_names)   297    298             for function_name, parameters in t.table.items():   299                 for name in parameters.keys():   300                     if name in keyword_names:   301                         break   302                 else:   303                     del t.table[function_name]   304    305         return self.paramtable   306    307     # Import methods.   308    309     def find_in_path(self, name):   310    311         """   312         Find the given module 'name' in the search path, returning None where no   313         such module could be found, or a 2-tuple from the 'find' method   314         otherwise.   315         """   316    317         for d in self.path:   318             m = self.find(d, name)   319             if m: return m   320         return None   321    322     def find(self, d, name):   323    324         """   325         In the directory 'd', find the given module 'name', where 'name' can   326         either refer to a single file module or to a package. Return None if the   327         'name' cannot be associated with either a file or a package directory,   328         or a 2-tuple from '_find_package' or '_find_module' otherwise.   329         """   330    331         m = self._find_package(d, name)   332         if m: return m   333         m = self._find_module(d, name)   334         if m: return m   335         return None   336    337     def _find_module(self, d, name):   338    339         """   340         In the directory 'd', find the given module 'name', returning None where   341         no suitable file exists in the directory, or a 2-tuple consisting of   342         None (indicating that no package directory is involved) and a filename   343         indicating the location of the module.   344         """   345    346         name_py = name + os.extsep + "py"   347         filename = self._find_file(d, name_py)   348         if filename:   349             return None, filename   350         return None   351    352     def _find_package(self, d, name):   353    354         """   355         In the directory 'd', find the given package 'name', returning None   356         where no suitable package directory exists, or a 2-tuple consisting of   357         a directory (indicating the location of the package directory itself)   358         and a filename indicating the location of the __init__.py module which   359         declares the package's top-level contents.   360         """   361    362         filename = self._find_file(d, name)   363         if filename:   364             init_py = "__init__" + os.path.extsep + "py"   365             init_py_filename = self._find_file(filename, init_py)   366             if init_py_filename:   367                 return filename, init_py_filename   368         return None   369    370     def _find_file(self, d, filename):   371    372         """   373         Return the filename obtained when searching the directory 'd' for the   374         given 'filename', or None if no actual file exists for the filename.   375         """   376    377         filename = os.path.join(d, filename)   378         if os.path.exists(filename):   379             return filename   380         else:   381             return None   382    383     def load(self, name, return_leaf=0):   384    385         """   386         Load the module or package with the given 'name'. Return an object   387         referencing the loaded module or package, or None if no such module or   388         package exists.   389         """   390    391         if self.modules.has_key(name) and self.modules[name].loaded:   392             #print "Cached (%s)" % name   393             return self.modules[name]   394         if self.verbose:   395             print "Loading", name   396    397         # Split the name into path components, and try to find the uppermost in   398         # the search path.   399    400         path = name.split(".")   401         m = self.find_in_path(path[0])   402         if not m:   403             if self.verbose:   404                 print "Not found (%s)" % path[0]   405             return None # NOTE: Import error.   406         d, filename = m   407    408         # Either acquire a reference to an already-imported module, or load the   409         # module from a file.   410    411         top = module = self.load_from_file(filename, path[0])   412    413         # For hierarchical names, traverse each path component...   414    415         if len(path) > 1:   416             if not d:   417                 if self.verbose:   418                     print "No package (%s)" % filename   419                 return None # NOTE: Import error (package not found).   420             else:   421                 self.add_submodules(d, module)   422    423             path_so_far = path[:1]   424             for p in path[1:]:   425                 path_so_far.append(p)   426    427                 # Find the package or module concerned.   428    429                 m = self.find(d, p)   430                 if not m:   431                     if self.verbose:   432                         print "Not found (%s)" % p   433                     return None # NOTE: Import error.   434                 d, filename = m   435                 module_name = ".".join(path_so_far)   436    437                 # Either reference an imported module or load one from a file.   438    439                 submodule = self.load_from_file(filename, module_name)   440    441                 if d:   442                     self.add_submodules(d, module)   443    444                 # Store the submodule within its parent module.   445    446                 module.set_module(p, submodule)   447                 module = submodule   448    449         # Return either the deepest or the uppermost module.   450    451         if return_leaf:   452             return module   453         else:   454             return top   455    456     def load_from_file(self, name, module_name=None):   457    458         """   459         Load the module with the given 'name' (which may be a full module path).   460         """   461    462         if module_name is None:   463             module_name = "__main__"   464    465         module = self.add_module(module_name)   466         if not module.loaded and module not in self.loading:   467             self.loading.add(module)   468             #print "Parsing", name   469             module.parse(name)   470             #print "Done", name   471             self.loading.remove(module)   472             module.loaded = 1   473    474         # Record the module.   475    476         #print "Loaded", module_name, "with namespace", module.namespace.keys()   477         return module   478    479     def add_module(self, module_name):   480    481         """   482         Return the module with the given 'module_name', adding a new module   483         object if one does not already exist.   484         """   485    486         if not self.modules.has_key(module_name):   487             self.modules[module_name] = module = micropython.inspect.InspectedModule(module_name, self)   488             self.modules_ordered.append(module)   489         else:   490             module = self.modules[module_name]   491         return module   492    493     def add_submodules(self, pathname, module):   494    495         """   496         Work around insufficient __all__ declarations and examine the directory   497         with the given 'pathname', adding submodules to the given 'module'.   498         """   499    500         for filename in os.listdir(pathname):   501             submodule, ext = os.path.splitext(filename)   502             if ext not in ("", ".py"):   503                 continue   504             module.set_module(submodule, self.add_module(module.name + "." + submodule))   505    506 # vim: tabstop=4 expandtab shiftwidth=4