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 def __init__(self, path=None, verbose=0): 54 55 """ 56 Initialise the importer with the given search 'path' - a list of 57 directories to search for Python modules. 58 59 The optional 'verbose' parameter causes output concerning the activities 60 of the object to be produced if set to a true value (not the default). 61 """ 62 63 self.path = path or [os.getcwd()] 64 self.verbose = verbose 65 self.modules = {"__builtins__" : micropython.inspect.builtins} 66 self.loading = set() 67 68 def vacuum(self): 69 70 "Tidy up the modules." 71 72 for name, module in self.modules.items(): 73 if module.loaded: 74 module.vacuum() 75 else: 76 del self.modules[name] 77 78 def get_modules(self): 79 80 "Return all modules known to the importer." 81 82 return self.modules.values() 83 84 def get_image(self, objtable=None, paramtable=None, with_builtins=0): 85 86 "Return a dictionary mapping modules to structures." 87 88 objtable = objtable or self.get_object_table() 89 paramtable = paramtable or self.get_parameter_table() 90 91 image = [] 92 93 for module_name, module in self.modules.items(): 94 if not with_builtins and module_name == "__builtins__": 95 continue 96 97 # Fix the attributes. 98 99 module.finalise_attributes() 100 101 pos = len(image) 102 103 # Position the module in the image and make a translation. 104 105 module.location = pos 106 trans = micropython.ast.Translation(module, objtable, paramtable) 107 108 # Append constants to the image. 109 110 for const in module.constants(): 111 const.location = pos 112 image.append(const) 113 pos += 1 114 115 # Add header details. 116 117 image.append(module) 118 pos += 1 119 120 # Append module attributes to the image. 121 122 attributes = module.module_attributes() 123 image += module.attributes_as_list() 124 pos += len(attributes.keys()) 125 126 # Append classes and functions to the image. 127 128 for obj in module.all_objects: 129 130 # Fix the attributes. 131 132 obj.finalise_attributes() 133 134 if isinstance(obj, micropython.inspect.Class): 135 136 # Position the class in the image. 137 138 obj.location = pos 139 140 # Add header details. 141 142 image.append(obj) 143 pos += 1 144 145 # Append class attributes to the image. 146 147 attributes = obj.class_attributes() 148 image += obj.attributes_as_list() 149 pos += len(attributes.keys()) 150 151 # Class-level code is generated separately. 152 # The code location is set within the code generation 153 # process for the module. 154 155 # NOTE: Generate module and function code here. 156 157 elif isinstance(obj, micropython.inspect.Function): 158 159 # Position the function in the image. 160 161 obj.location = pos 162 163 # Add header details. 164 165 image.append(obj) 166 pos += 1 167 168 # Append the function code to the image. 169 170 obj.code_location = pos 171 code = trans.get_code(obj) 172 image += code 173 pos += len(code) 174 175 # Remember the position of the module code. 176 177 module.code_location = pos 178 179 # Append the module top-level code to the image. 180 181 code = trans.get_module_code() 182 image += code 183 pos += len(code) 184 185 return image 186 187 def get_object_table(self): 188 189 "Return a table with details of attributes for classes and modules." 190 191 t = micropython.table.Table() 192 for module in self.get_modules(): 193 t.add(module.full_name(), module.module_attributes()) 194 for obj in module.all_objects: 195 if isinstance(obj, micropython.inspect.Class): 196 t.add(obj.full_name(), obj.all_attributes()) 197 return t 198 199 def get_parameter_table(self): 200 201 "Return a table with details of parameters for functions and methods." 202 203 t = micropython.table.Table() 204 for module in self.get_modules(): 205 for obj in module.all_objects: 206 if isinstance(obj, micropython.inspect.Function): 207 t.add(obj.full_name(), obj.parameters()) 208 return t 209 210 # Import methods. 211 212 def find_in_path(self, name): 213 214 """ 215 Find the given module 'name' in the search path, returning None where no 216 such module could be found, or a 2-tuple from the 'find' method 217 otherwise. 218 """ 219 220 for d in self.path: 221 m = self.find(d, name) 222 if m: return m 223 return None 224 225 def find(self, d, name): 226 227 """ 228 In the directory 'd', find the given module 'name', where 'name' can 229 either refer to a single file module or to a package. Return None if the 230 'name' cannot be associated with either a file or a package directory, 231 or a 2-tuple from '_find_package' or '_find_module' otherwise. 232 """ 233 234 m = self._find_package(d, name) 235 if m: return m 236 m = self._find_module(d, name) 237 if m: return m 238 return None 239 240 def _find_module(self, d, name): 241 242 """ 243 In the directory 'd', find the given module 'name', returning None where 244 no suitable file exists in the directory, or a 2-tuple consisting of 245 None (indicating that no package directory is involved) and a filename 246 indicating the location of the module. 247 """ 248 249 name_py = name + os.extsep + "py" 250 filename = self._find_file(d, name_py) 251 if filename: 252 return None, filename 253 return None 254 255 def _find_package(self, d, name): 256 257 """ 258 In the directory 'd', find the given package 'name', returning None 259 where no suitable package directory exists, or a 2-tuple consisting of 260 a directory (indicating the location of the package directory itself) 261 and a filename indicating the location of the __init__.py module which 262 declares the package's top-level contents. 263 """ 264 265 filename = self._find_file(d, name) 266 if filename: 267 init_py = "__init__" + os.path.extsep + "py" 268 init_py_filename = self._find_file(filename, init_py) 269 if init_py_filename: 270 return filename, init_py_filename 271 return None 272 273 def _find_file(self, d, filename): 274 275 """ 276 Return the filename obtained when searching the directory 'd' for the 277 given 'filename', or None if no actual file exists for the filename. 278 """ 279 280 filename = os.path.join(d, filename) 281 if os.path.exists(filename): 282 return filename 283 else: 284 return None 285 286 def load(self, name, return_leaf=0): 287 288 """ 289 Load the module or package with the given 'name'. Return an object 290 referencing the loaded module or package, or None if no such module or 291 package exists. 292 """ 293 294 if self.modules.has_key(name) and self.modules[name].loaded: 295 #print "Cached (%s)" % name 296 return self.modules[name] 297 if self.verbose: 298 print "Loading", name 299 300 # Split the name into path components, and try to find the uppermost in 301 # the search path. 302 303 path = name.split(".") 304 m = self.find_in_path(path[0]) 305 if not m: 306 if self.verbose: 307 print "Not found (%s)" % path[0] 308 return None # NOTE: Import error. 309 d, filename = m 310 311 # Either acquire a reference to an already-imported module, or load the 312 # module from a file. 313 314 top = module = self.load_from_file(filename, path[0]) 315 316 # For hierarchical names, traverse each path component... 317 318 if len(path) > 1: 319 if not d: 320 if self.verbose: 321 print "No package (%s)" % filename 322 return None # NOTE: Import error (package not found). 323 else: 324 self.add_submodules(d, module) 325 326 path_so_far = path[:1] 327 for p in path[1:]: 328 path_so_far.append(p) 329 330 # Find the package or module concerned. 331 332 m = self.find(d, p) 333 if not m: 334 if self.verbose: 335 print "Not found (%s)" % p 336 return None # NOTE: Import error. 337 d, filename = m 338 module_name = ".".join(path_so_far) 339 340 # Either reference an imported module or load one from a file. 341 342 submodule = self.load_from_file(filename, module_name) 343 344 if d: 345 self.add_submodules(d, module) 346 347 # Store the submodule within its parent module. 348 349 module.set_module(p, submodule) 350 module = submodule 351 352 # Return either the deepest or the uppermost module. 353 354 if return_leaf: 355 return module 356 else: 357 return top 358 359 def load_from_file(self, name, module_name=None): 360 361 """ 362 Load the module with the given 'name' (which may be a full module path). 363 """ 364 365 if module_name is None: 366 module_name = "__main__" 367 368 module = self.add_module(module_name) 369 if not module.loaded and module not in self.loading: 370 self.loading.add(module) 371 #print "Parsing", name 372 module.parse(name) 373 #print "Done", name 374 self.loading.remove(module) 375 module.loaded = 1 376 377 # Record the module. 378 379 #print "Loaded", module_name, "with namespace", module.namespace.keys() 380 return module 381 382 def add_module(self, module_name): 383 384 """ 385 Return the module with the given 'module_name', adding a new module 386 object if one does not already exist. 387 """ 388 389 if not self.modules.has_key(module_name): 390 self.modules[module_name] = module = micropython.inspect.InspectedModule(module_name, self) 391 else: 392 module = self.modules[module_name] 393 return module 394 395 def add_submodules(self, pathname, module): 396 397 """ 398 Work around insufficient __all__ declarations and examine the directory 399 with the given 'pathname', adding submodules to the given 'module'. 400 """ 401 402 for filename in os.listdir(pathname): 403 submodule, ext = os.path.splitext(filename) 404 if ext not in ("", ".py"): 405 continue 406 module.set_module(submodule, self.add_module(module.name + "." + submodule)) 407 408 # vim: tabstop=4 expandtab shiftwidth=4