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