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 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 31 Such importer objects are the most convenient mechanism through which the 32 functionality of the micropython package may be accessed. 33 """ 34 35 import micropython.inspect 36 import os 37 try: 38 set 39 except NameError: 40 from sets import Set as set 41 42 class Importer: 43 44 "An import machine, searching for and loading modules." 45 46 def __init__(self, path=None): 47 48 """ 49 Initialise the importer with the given search 'path' - a list of 50 directories to search for Python modules. 51 """ 52 53 self.path = path or [os.getcwd()] 54 self.modules = {} 55 self.loading = set() 56 57 def get_modules(self): 58 59 "Return all modules known to the importer." 60 61 return self.modules.values() 62 63 def find_in_path(self, name): 64 65 """ 66 Find the given module 'name' in the search path, returning None where no 67 such module could be found, or a 2-tuple from the 'find' method 68 otherwise. 69 """ 70 71 for d in self.path: 72 m = self.find(d, name) 73 if m: return m 74 return None 75 76 def find(self, d, name): 77 78 """ 79 In the directory 'd', find the given module 'name', where 'name' can 80 either refer to a single file module or to a package. Return None if the 81 'name' cannot be associated with either a file or a package directory, 82 or a 2-tuple from '_find_package' or '_find_module' otherwise. 83 """ 84 85 m = self._find_package(d, name) 86 if m: return m 87 m = self._find_module(d, name) 88 if m: return m 89 return None 90 91 def _find_module(self, d, name): 92 93 """ 94 In the directory 'd', find the given module 'name', returning None where 95 no suitable file exists in the directory, or a 2-tuple consisting of 96 None (indicating that no package directory is involved) and a filename 97 indicating the location of the module. 98 """ 99 100 name_py = name + os.extsep + "py" 101 filename = self._find_file(d, name_py) 102 if filename: 103 return None, filename 104 return None 105 106 def _find_package(self, d, name): 107 108 """ 109 In the directory 'd', find the given package 'name', returning None 110 where no suitable package directory exists, or a 2-tuple consisting of 111 a directory (indicating the location of the package directory itself) 112 and a filename indicating the location of the __init__.py module which 113 declares the package's top-level contents. 114 """ 115 116 filename = self._find_file(d, name) 117 if filename: 118 init_py = "__init__" + os.path.extsep + "py" 119 init_py_filename = self._find_file(filename, init_py) 120 if init_py_filename: 121 return filename, init_py_filename 122 return None 123 124 def _find_file(self, d, filename): 125 126 """ 127 Return the filename obtained when searching the directory 'd' for the 128 given 'filename', or None if no actual file exists for the filename. 129 """ 130 131 filename = os.path.join(d, filename) 132 if os.path.exists(filename): 133 return filename 134 else: 135 return None 136 137 def load(self, name, return_leaf=0): 138 139 """ 140 Load the module or package with the given 'name'. Return an object 141 referencing the loaded module or package, or None if no such module or 142 package exists. 143 """ 144 145 print "Loading", name 146 if self.modules.has_key(name) and self.modules[name].loaded: 147 print "Cached", name 148 return self.modules[name] 149 150 # Split the name into path components, and try to find the uppermost in 151 # the search path. 152 153 path = name.split(".") 154 m = self.find_in_path(path[0]) 155 if not m: 156 print "Not found", path[0] 157 return None # NOTE: Import error. 158 d, filename = m 159 160 # Either acquire a reference to an already-imported module, or load the 161 # module from a file. 162 163 top = module = self.load_from_file(filename, path[0]) 164 165 # For hierarchical names, traverse each path component... 166 167 if len(path) > 1: 168 if not d: 169 print "No package", filename 170 return None # NOTE: Import error (package not found). 171 else: 172 self.add_submodules(d, module) 173 174 path_so_far = path[:1] 175 for p in path[1:]: 176 path_so_far.append(p) 177 178 # Find the package or module concerned. 179 180 m = self.find(d, p) 181 if not m: 182 print "Not found", p 183 return None # NOTE: Import error. 184 d, filename = m 185 module_name = ".".join(path_so_far) 186 187 # Either reference an imported module or load one from a file. 188 189 submodule = self.load_from_file(filename, module_name) 190 191 if d: 192 self.add_submodules(d, module) 193 194 # Store the submodule within its parent module. 195 196 module.namespace[p] = submodule 197 module = submodule 198 199 # Return either the deepest or the uppermost module. 200 201 if return_leaf: 202 return module 203 else: 204 return top 205 206 def load_from_file(self, name, module_name=None): 207 208 """ 209 Load the module with the given 'name' (which may be a full module path). 210 """ 211 212 if module_name is None: 213 module_name = "__main__" 214 215 module = self.add_module(module_name) 216 if not module.loaded and module not in self.loading: 217 self.loading.add(module) 218 print "Parsing", name 219 module.parse(name) 220 print "Done", name 221 self.loading.remove(module) 222 module.loaded = 1 223 224 # Record the module. 225 226 print "Loaded", module_name #, "with namespace", module.namespace.keys() 227 return module 228 229 def add_module(self, module_name): 230 231 """ 232 Return the module with the given 'module_name', adding a new module 233 object if one does not already exist. 234 """ 235 236 if not self.modules.has_key(module_name): 237 self.modules[module_name] = module = micropython.inspect.Module(module_name, self) 238 else: 239 module = self.modules[module_name] 240 return module 241 242 def add_submodules(self, pathname, module): 243 244 """ 245 Work around insufficient __all__ declarations and examine the directory 246 with the given 'pathname', adding submodules to the given 'module'. 247 """ 248 249 for filename in os.listdir(pathname): 250 submodule = os.path.splitext(filename)[0] 251 module.namespace[submodule] = self.add_module(module.name + "." + submodule) 252 253 # vim: tabstop=4 expandtab shiftwidth=4