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