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