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