1 #!/usr/bin/env python 2 3 """ 4 Inspect source files, obtaining details of classes and attributes. 5 6 Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 21 -------- 22 23 The results of inspecting a module are as follows: 24 25 Constants 26 --------- 27 28 All constants identified within the code shall be registered. 29 30 Classes 31 ------- 32 33 All global classes shall be registered; local classes (within functions) or 34 nested classes (within classes) are not currently registered. 35 36 Base classes must be detected and constant. 37 38 All classes without bases are made to inherit from __builtins__.object in order 39 to support some standard methods. 40 41 Functions 42 --------- 43 44 All functions and lambda definitions shall be registered. 45 46 Namespaces 47 ---------- 48 49 Modules define their own "global" namespace, within which classes, functions 50 and lambda definitions establish a hierarchy of namespaces. 51 52 Only local, global and built-in namespaces are recognised; closures are not 53 supported. 54 55 Assignments 56 ----------- 57 58 Name assignment and attribute assignment involving modules and classes cause 59 names to be associated with values within namespaces. 60 61 Any assignments within loops are considered to cause the targets of such 62 assignments to provide non-constant values. 63 64 Assignments to names are only really considered to cause the targets of such 65 assignments to provide constant values if the targets reside in the module 66 namespace or in class namespaces, subject to the above conditions. 67 68 Assignments to names within functions are not generally considered to cause the 69 targets of such assignments to provide constant values since functions can be 70 invoked many times with different inputs. However, there may be benefits in 71 considering a local to be constant within a single invocation. 72 """ 73 74 from micropython.common import * 75 from micropython.data import * 76 import compiler.ast 77 from compiler.visitor import ASTVisitor 78 79 # Program visitors. 80 81 class InspectedModule(ASTVisitor, Module): 82 83 """ 84 An inspected module, providing core details via the Module superclass, but 85 capable of being used as an AST visitor. 86 """ 87 88 def __init__(self, name, importer): 89 90 """ 91 Initialise this visitor with a module 'name' and an 'importer' which is 92 used to provide access to other modules when required. 93 """ 94 95 ASTVisitor.__init__(self) 96 Module.__init__(self, name) 97 self.visitor = self 98 99 # Import machinery links. 100 101 self.importer = importer 102 self.builtins = self.importer.modules.get("__builtins__") 103 self.loaded = 0 104 105 # Constant records. 106 107 self.constant_values = importer.constant_values 108 109 # Current expression state. 110 111 self.expr = None 112 113 # Namespace state. 114 115 self.in_init = 0 # Find instance attributes in __init__ methods. 116 self.in_method = 0 # Find instance attributes in all methods. 117 self.in_loop = 0 # Note loop "membership", affecting assignments. 118 self.namespaces = [] 119 self.module = None 120 121 def parse(self, filename): 122 123 "Parse the file having the given 'filename'." 124 125 module = compiler.parseFile(filename) 126 self.process(module) 127 128 def process(self, module): 129 130 "Process the given 'module'." 131 132 self.astnode = self.module = module 133 processed = self.dispatch(module) 134 if self.has_key("__all__"): 135 all = self["__all__"] 136 if isinstance(all, compiler.ast.List): 137 for n in all.nodes: 138 self[n.value] = self.importer.add_module(self.name + "." + n.value) 139 return processed 140 141 def vacuum(self): 142 143 "Vacuum the module namespace, removing unloaded module references." 144 145 for name, value in self.items(): 146 if isinstance(value, Module) and not value.loaded: 147 del self[name] 148 149 # Complain about globals not initialised at the module level. 150 151 if isinstance(value, Global): 152 print "Warning: global %r in module %r not initialised at the module level." % (name, self.name) 153 154 # Namespace methods. 155 156 def store(self, name, obj): 157 158 "Record attribute or local 'name', storing 'obj'." 159 160 if not self.namespaces: 161 self.set(name, obj, not self.in_loop) 162 else: 163 self.namespaces[-1].set(name, obj, not self.in_loop) 164 165 # Record all non-local objects. 166 167 if not (self.namespaces and isinstance(self.namespaces[-1], Function)): 168 self.all_objects.add(obj) 169 170 def store_lambda(self, obj): 171 172 "Store a lambda function 'obj'." 173 174 self.all_objects.add(obj) 175 176 def store_class_attr(self, name): 177 178 "Record class attribute 'name' in the current class." 179 180 if self.in_method and self.namespaces[-2].has_key(name): 181 182 if isinstance(self.expr, Attr): 183 assigned_value = self.expr.value 184 else: 185 assigned_value = self.expr 186 187 self.namespaces[-2].set(name, assigned_value, 0) 188 return 1 189 190 return 0 191 192 def store_instance_attr(self, name): 193 194 "Record instance attribute 'name' in the current class." 195 196 if self.in_method: 197 198 # Current namespace is the function. 199 # Previous namespace is the class. 200 201 self.namespaces[-2].add_instance_attribute(name) 202 203 def get_parent(self): 204 205 "Return the parent (or most recent) namespace currently exposed." 206 207 return (self.namespaces[-1:] or [self])[0] 208 209 # Visitor methods. 210 211 def default(self, node, *args): 212 raise InspectError(self.full_name(), node, "Node class %r is not supported." % node.__class__) 213 214 def dispatch(self, node, *args): 215 return ASTVisitor.dispatch(self, node, *args) 216 217 def NOP(self, node): 218 for n in node.getChildNodes(): 219 self.dispatch(n) 220 return None 221 222 def OP(self, node): 223 for n in node.getChildNodes(): 224 self.dispatch(n) 225 return Instance() 226 227 def _visitConst(self, node): 228 return self._make_constant(node.value) 229 230 def _make_constant(self, value): 231 if not self.constant_values.has_key(value): 232 const = Const(value) 233 self.constant_values[value] = const 234 return self.constant_values[value] 235 236 def _visitFunction(self, node, name): 237 238 """ 239 Return a function object for the function defined by 'node' with the 240 given 'name'. If a lambda expression is being visited, 'name' should be 241 None. 242 """ 243 244 function = Function( 245 name, 246 self.get_parent(), 247 node.argnames, 248 node.defaults, 249 (node.flags & 4 != 0), 250 (node.flags & 8 != 0), 251 self, 252 node 253 ) 254 255 # Make a back reference from the node for code generation. 256 257 node.unit = function 258 259 # Process the defaults. 260 261 for n in node.defaults: 262 self.expr = self.dispatch(n) 263 if isinstance(self.expr, Attr): 264 function.store_default(self.expr.value) 265 else: 266 function.store_default(self.expr) 267 268 # Enter the function. 269 270 self.namespaces.append(function) 271 272 # Current namespace is the function. 273 # Previous namespace is the class. 274 275 if len(self.namespaces) > 1 and isinstance(self.namespaces[-2], Class): 276 if name == "__init__": 277 self.in_init = 1 278 self.in_method = 1 279 280 self.dispatch(node.code) 281 self.in_init = 0 282 self.in_method = 0 283 self.namespaces.pop() 284 285 if name is not None: 286 self.store(name, function) 287 else: 288 self.store_lambda(function) 289 290 return function 291 292 visitAdd = OP 293 294 visitAnd = OP 295 296 visitAssert = NOP 297 298 def visitAssign(self, node): 299 self.expr = self.dispatch(node.expr) 300 for n in node.nodes: 301 self.dispatch(n) 302 return None 303 304 def visitAssAttr(self, node): 305 expr = self.dispatch(node.expr) 306 if isinstance(expr, Attr) and expr.name == "self": 307 if not self.store_class_attr(node.attrname): 308 self.store_instance_attr(node.attrname) 309 return None 310 311 def visitAssList(self, node): 312 for n in node.nodes: 313 self.dispatch(n) 314 return None 315 316 def visitAssName(self, node): 317 if isinstance(self.expr, Attr): 318 self.store(node.name, self.expr.value) 319 else: 320 self.store(node.name, self.expr) 321 return None 322 323 visitAssTuple = visitAssList 324 325 visitAugAssign = OP 326 327 visitBackquote = OP 328 329 visitBitand = OP 330 331 visitBitor = OP 332 333 visitBitxor = OP 334 335 visitBreak = NOP 336 337 visitCallFunc = OP 338 339 def visitClass(self, node): 340 341 """ 342 Register the class at the given 'node' subject to the restrictions 343 mentioned in the module docstring. 344 """ 345 346 if self.namespaces: 347 print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name()) 348 return None 349 else: 350 cls = Class(node.name, self.get_parent(), self, node) 351 352 # Visit the base class expressions, attempting to find concrete 353 # definitions of classes. 354 355 for base in node.bases: 356 expr = self.dispatch(base) 357 if isinstance(expr, Attr): 358 if expr.assignments != 1: 359 raise InspectError(self.full_name(), node, 360 "Base class %r for %r is not constant." % (base, cls.full_name())) 361 else: 362 cls.add_base(expr.value) 363 else: # if expr is None: 364 raise InspectError(self.full_name(), node, 365 "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name())) 366 367 # NOTE: Potentially dubious measure to permit __init__ availability. 368 # If no bases exist, adopt the 'object' class. 369 370 if not node.bases and not (self.name == "__builtins__" and node.name == "object") : 371 expr = self.dispatch(compiler.ast.Name("object")) 372 cls.add_base(expr.value) 373 374 # Make a back reference from the node for code generation. 375 376 node.unit = cls 377 378 # Make an entry for the class. 379 380 self.store(node.name, cls) 381 382 # Process the class body. 383 384 self.namespaces.append(cls) 385 self.dispatch(node.code) 386 self.namespaces.pop() 387 388 return cls 389 390 visitCompare = OP 391 392 def visitConst(self, node): 393 return self._visitConst(node) 394 395 visitContinue = NOP 396 397 visitDecorators = NOP 398 399 visitDict = OP 400 401 visitDiscard = NOP 402 403 visitDiv = OP 404 405 visitEllipsis = NOP 406 407 visitExec = NOP 408 409 visitExpression = OP 410 411 visitFloorDiv = OP 412 413 def visitFor(self, node): 414 self.in_loop = 1 415 self.NOP(node) 416 self.in_loop = 0 417 return None 418 419 def visitFrom(self, node): 420 module = self.importer.load(node.modname, 1) 421 422 #if module is None: 423 # print "Warning:", node.modname, "not imported." 424 425 for name, alias in node.names: 426 if name != "*": 427 if module is not None and module.namespace.has_key(name): 428 attr = module[name] 429 self.store(alias or name, attr.value) 430 if isinstance(attr, Module) and not attr.loaded: 431 self.importer.load(attr.name) 432 433 # Support the import of names from missing modules. 434 435 else: 436 self.store(alias or name, UnresolvedName(name, node.modname, self)) 437 else: 438 if module is not None: 439 for n in module.namespace.keys(): 440 attr = module[n] 441 self.store(n, attr.value) 442 if isinstance(attr, Module) and not attr.loaded: 443 self.importer.load(attr.name) 444 445 return None 446 447 def visitFunction(self, node): 448 return self._visitFunction(node, node.name) 449 450 visitGenExpr = OP 451 452 visitGenExprFor = NOP 453 454 visitGenExprIf = NOP 455 456 visitGenExprInner = NOP 457 458 def visitGetattr(self, node): 459 expr = self.dispatch(node.expr) 460 if isinstance(expr, Attr): 461 value = expr.value 462 if isinstance(value, (Class, Module)): 463 return value.namespace.get(node.attrname) 464 elif isinstance(value, UnresolvedName): 465 return UnresolvedName(node.attrname, value.full_name(), self) 466 else: 467 return None 468 if self.builtins is not None: 469 return self.builtins.get(node.attrname) 470 else: 471 return UnresolvedName(node.attrname, value.full_name(), self) 472 473 def visitGlobal(self, node): 474 if self.namespaces: 475 for name in node.names: 476 self.namespaces[-1].make_global(name) 477 478 # Record a global entry for the name in the module. 479 480 if not self.has_key(name): 481 self[name] = Global() 482 483 def visitIf(self, node): 484 for test, body in node.tests: 485 self.dispatch(body) 486 if node.else_ is not None: 487 self.dispatch(node.else_) 488 return None 489 490 visitIfExp = NOP 491 492 def visitImport(self, node): 493 for name, alias in node.names: 494 if alias is not None: 495 self.store(alias, self.importer.load(name, 1) or UnresolvedName(None, name, self)) 496 else: 497 self.store(name.split(".")[0], self.importer.load(name) or UnresolvedName(None, name.split(".")[0], self)) 498 499 return None 500 501 visitInvert = OP 502 503 def visitKeyword(self, node): 504 self.dispatch(node.expr) 505 self._visitConst(node) 506 self.keyword_names.add(node.name) 507 return None 508 509 def visitLambda(self, node): 510 return self._visitFunction(node, None) 511 512 visitLeftShift = OP 513 514 visitList = OP 515 516 visitListComp = OP 517 518 visitListCompFor = NOP 519 520 visitListCompIf = NOP 521 522 visitMod = OP 523 524 def visitModule(self, node): 525 return self.dispatch(node.node) 526 527 visitMul = OP 528 529 def visitName(self, node): 530 name = node.name 531 if name == "None": 532 return self._make_constant(None) 533 elif self.namespaces and self.namespaces[-1].has_key(name): 534 return self.namespaces[-1][name] 535 elif self.has_key(name): 536 return self[name] 537 elif self.builtins is not None and self.builtins.has_key(name): 538 return self.builtins[name] 539 else: 540 return None 541 542 visitNot = OP 543 544 visitOr = OP 545 546 visitPass = NOP 547 548 visitPower = OP 549 550 visitPrint = NOP 551 552 visitPrintnl = NOP 553 554 visitRaise = NOP 555 556 visitReturn = NOP 557 558 visitRightShift = OP 559 560 visitSlice = OP 561 562 visitSliceobj = OP 563 564 def visitStmt(self, node): 565 for n in node.nodes: 566 self.dispatch(n) 567 return None 568 569 visitSub = OP 570 571 visitSubscript = OP 572 573 def visitTryExcept(self, node): 574 self.dispatch(node.body) 575 for name, var, n in node.handlers: 576 self.dispatch(n) 577 if node.else_ is not None: 578 self.dispatch(node.else_) 579 return None 580 581 visitTryFinally = NOP 582 583 visitTuple = OP 584 585 visitUnaryAdd = OP 586 587 visitUnarySub = OP 588 589 def visitWhile(self, node): 590 self.in_loop = 1 591 self.NOP(node) 592 self.in_loop = 0 593 return None 594 595 visitWith = NOP 596 597 visitYield = NOP 598 599 class Global: 600 601 """ 602 A reference to an object assigned to a global from outside the module 603 top-level. 604 """ 605 606 pass 607 608 # vim: tabstop=4 expandtab shiftwidth=4