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_module_attr(self, name, module): 177 178 """ 179 Record module attribute 'name' in the given 'module' using the current 180 expression. 181 """ 182 183 if isinstance(self.expr, Attr): 184 assigned_value = self.expr.value 185 else: 186 assigned_value = self.expr 187 188 module.set(name, assigned_value, 0) 189 190 def store_class_attr(self, name): 191 192 """ 193 Record class attribute 'name' in the current class using the current 194 expression. 195 """ 196 197 if self.in_method and self.namespaces[-2].has_key(name): 198 199 if isinstance(self.expr, Attr): 200 assigned_value = self.expr.value 201 else: 202 assigned_value = self.expr 203 204 self.namespaces[-2].set(name, assigned_value, 0) 205 return 1 206 207 return 0 208 209 def store_instance_attr(self, name): 210 211 "Record instance attribute 'name' in the current class." 212 213 if self.in_method: 214 215 # Current namespace is the function. 216 # Previous namespace is the class. 217 218 self.namespaces[-2].add_instance_attribute(name) 219 220 def get_parent(self): 221 222 "Return the parent (or most recent) namespace currently exposed." 223 224 return (self.namespaces[-1:] or [self])[0] 225 226 # Visitor methods. 227 228 def default(self, node, *args): 229 raise InspectError(self.full_name(), node, "Node class %r is not supported." % node.__class__) 230 231 def dispatch(self, node, *args): 232 return ASTVisitor.dispatch(self, node, *args) 233 234 def NOP(self, node): 235 for n in node.getChildNodes(): 236 self.dispatch(n) 237 return None 238 239 def OP(self, node): 240 for n in node.getChildNodes(): 241 self.dispatch(n) 242 return Instance() 243 244 def _make_constant(self, value): 245 if not self.constant_values.has_key(value): 246 const = Const(value) 247 self.constant_values[value] = const 248 return self.constant_values[value] 249 250 def _visitFunction(self, node, name): 251 252 """ 253 Return a function object for the function defined by 'node' with the 254 given 'name'. If a lambda expression is being visited, 'name' should be 255 None. 256 """ 257 258 function = Function( 259 name, 260 self.get_parent(), 261 node.argnames, 262 node.defaults, 263 (node.flags & 4 != 0), 264 (node.flags & 8 != 0), 265 self, 266 node 267 ) 268 269 # Make a back reference from the node for code generation. 270 271 node.unit = function 272 273 # Process the defaults. 274 275 for n in node.defaults: 276 self.expr = self.dispatch(n) 277 if isinstance(self.expr, Attr): 278 function.store_default(self.expr.value) 279 else: 280 function.store_default(self.expr) 281 282 # Enter the function. 283 284 self.namespaces.append(function) 285 286 # Current namespace is the function. 287 # Previous namespace is the class. 288 289 if len(self.namespaces) > 1 and isinstance(self.namespaces[-2], Class): 290 if name == "__init__": 291 self.in_init = 1 292 self.in_method = 1 293 294 self.dispatch(node.code) 295 self.in_init = 0 296 self.in_method = 0 297 self.namespaces.pop() 298 299 if name is not None: 300 self.store(name, function) 301 else: 302 self.store_lambda(function) 303 if node.defaults: 304 return None # NOTE: See lambda code in the ast module. 305 306 return function 307 308 visitAdd = OP 309 310 visitAnd = OP 311 312 visitAssert = NOP 313 314 def visitAssign(self, node): 315 self.expr = self.dispatch(node.expr) 316 for n in node.nodes: 317 self.dispatch(n) 318 return None 319 320 def visitAssAttr(self, node): 321 expr = self.dispatch(node.expr) 322 if isinstance(expr, Attr): 323 if expr.name == "self": 324 if not self.store_class_attr(node.attrname): 325 self.store_instance_attr(node.attrname) 326 elif isinstance(expr.value, Module): 327 self.store_module_attr(node.attrname, expr.value) 328 print "Warning: attribute %r of module %r set outside the module." % (node.attrname, expr.value.name) 329 return None 330 331 def visitAssList(self, node): 332 for n in node.nodes: 333 self.dispatch(n) 334 return None 335 336 def visitAssName(self, node): 337 self.store(node.name, self.expr) 338 return None 339 340 visitAssTuple = visitAssList 341 342 visitAugAssign = OP 343 344 visitBackquote = OP 345 346 visitBitand = OP 347 348 visitBitor = OP 349 350 visitBitxor = OP 351 352 visitBreak = NOP 353 354 visitCallFunc = OP 355 356 def visitClass(self, node): 357 358 """ 359 Register the class at the given 'node' subject to the restrictions 360 mentioned in the module docstring. 361 """ 362 363 if self.namespaces: 364 print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name()) 365 return None 366 else: 367 cls = Class(node.name, self.get_parent(), self, node) 368 369 # Visit the base class expressions, attempting to find concrete 370 # definitions of classes. 371 372 for base in node.bases: 373 expr = self.dispatch(base) 374 if isinstance(expr, Attr): 375 if expr.assignments != 1: 376 raise InspectError(self.full_name(), node, 377 "Base class %r for %r is not constant." % (base, cls.full_name())) 378 else: 379 cls.add_base(expr.value) 380 else: # if expr is None: 381 raise InspectError(self.full_name(), node, 382 "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name())) 383 384 # NOTE: Potentially dubious measure to permit __init__ availability. 385 # If no bases exist, adopt the 'object' class. 386 387 if not node.bases and not (self.name == "__builtins__" and node.name == "object") : 388 expr = self.dispatch(compiler.ast.Name("object")) 389 cls.add_base(expr.value) 390 391 # Make a back reference from the node for code generation. 392 393 node.unit = cls 394 395 # Make an entry for the class. 396 397 self.store(node.name, cls) 398 399 # Process the class body. 400 401 self.namespaces.append(cls) 402 self.dispatch(node.code) 403 self.namespaces.pop() 404 405 return cls 406 407 visitCompare = OP 408 409 def visitConst(self, node): 410 return self._make_constant(node.value) 411 412 visitContinue = NOP 413 414 visitDecorators = NOP 415 416 visitDict = OP 417 418 visitDiscard = NOP 419 420 visitDiv = OP 421 422 visitEllipsis = NOP 423 424 visitExec = NOP 425 426 visitExpression = OP 427 428 visitFloorDiv = OP 429 430 def visitFor(self, node): 431 self.in_loop = 1 432 self.NOP(node) 433 self.in_loop = 0 434 return None 435 436 def visitFrom(self, node): 437 module = self.importer.load(node.modname, 1) 438 439 #if module is None: 440 # print "Warning:", node.modname, "not imported." 441 442 for name, alias in node.names: 443 if name != "*": 444 if module is not None and module.namespace.has_key(name): 445 attr = module[name] 446 self.store(alias or name, attr.value) 447 if isinstance(attr, Module) and not attr.loaded: 448 self.importer.load(attr.name) 449 450 # Support the import of names from missing modules. 451 452 else: 453 self.store(alias or name, UnresolvedName(name, node.modname, self)) 454 else: 455 if module is not None: 456 for n in module.namespace.keys(): 457 attr = module[n] 458 self.store(n, attr.value) 459 if isinstance(attr, Module) and not attr.loaded: 460 self.importer.load(attr.name) 461 462 return None 463 464 def visitFunction(self, node): 465 return self._visitFunction(node, node.name) 466 467 visitGenExpr = OP 468 469 visitGenExprFor = NOP 470 471 visitGenExprIf = NOP 472 473 visitGenExprInner = NOP 474 475 def visitGetattr(self, node): 476 expr = self.dispatch(node.expr) 477 if isinstance(expr, Attr): 478 value = expr.value 479 if isinstance(value, (Class, Module)): 480 return value.namespace.get(node.attrname) 481 elif isinstance(value, UnresolvedName): 482 return UnresolvedName(node.attrname, value.full_name(), self) 483 else: 484 return None 485 if self.builtins is not None: 486 return self.builtins.get(node.attrname) 487 else: 488 return UnresolvedName(node.attrname, value.full_name(), self) 489 490 def visitGlobal(self, node): 491 if self.namespaces: 492 for name in node.names: 493 self.namespaces[-1].make_global(name) 494 495 # Record a global entry for the name in the module. 496 497 if not self.has_key(name): 498 self[name] = Global() 499 500 def visitIf(self, node): 501 for test, body in node.tests: 502 self.dispatch(body) 503 if node.else_ is not None: 504 self.dispatch(node.else_) 505 return None 506 507 visitIfExp = NOP 508 509 def visitImport(self, node): 510 for name, alias in node.names: 511 if alias is not None: 512 self.store(alias, self.importer.load(name, 1) or UnresolvedName(None, name, self)) 513 else: 514 self.store(name.split(".")[0], self.importer.load(name) or UnresolvedName(None, name.split(".")[0], self)) 515 516 return None 517 518 visitInvert = OP 519 520 def visitKeyword(self, node): 521 self.dispatch(node.expr) 522 self._make_constant(node.name) 523 self.keyword_names.add(node.name) 524 return None 525 526 def visitLambda(self, node): 527 return self._visitFunction(node, None) 528 529 visitLeftShift = OP 530 531 visitList = OP 532 533 visitListComp = OP 534 535 visitListCompFor = NOP 536 537 visitListCompIf = NOP 538 539 visitMod = OP 540 541 def visitModule(self, node): 542 return self.dispatch(node.node) 543 544 visitMul = OP 545 546 def visitName(self, node): 547 name = node.name 548 if name == "None": 549 return self._make_constant(None) 550 elif self.namespaces and self.namespaces[-1].has_key(name): 551 return self.namespaces[-1][name] 552 elif self.has_key(name): 553 return self[name] 554 elif self.builtins is not None and self.builtins.has_key(name): 555 return self.builtins[name] 556 else: 557 return None 558 559 visitNot = OP 560 561 visitOr = OP 562 563 visitPass = NOP 564 565 visitPower = OP 566 567 visitPrint = NOP 568 569 visitPrintnl = NOP 570 571 visitRaise = NOP 572 573 visitReturn = NOP 574 575 visitRightShift = OP 576 577 visitSlice = OP 578 579 visitSliceobj = OP 580 581 def visitStmt(self, node): 582 for n in node.nodes: 583 self.dispatch(n) 584 return None 585 586 visitSub = OP 587 588 visitSubscript = OP 589 590 def visitTryExcept(self, node): 591 self.dispatch(node.body) 592 for name, var, n in node.handlers: 593 594 # Establish the local for the handler. 595 596 if var is not None: 597 self.dispatch(var) 598 if n is not None: 599 self.dispatch(n) 600 if node.else_ is not None: 601 self.dispatch(node.else_) 602 return None 603 604 visitTryFinally = NOP 605 606 visitTuple = OP 607 608 visitUnaryAdd = OP 609 610 visitUnarySub = OP 611 612 def visitWhile(self, node): 613 self.in_loop = 1 614 self.NOP(node) 615 self.in_loop = 0 616 return None 617 618 visitWith = NOP 619 620 visitYield = NOP 621 622 class Global: 623 624 """ 625 A reference to an object assigned to a global from outside the module 626 top-level. 627 """ 628 629 pass 630 631 # vim: tabstop=4 expandtab shiftwidth=4