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