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 i, n in enumerate(node.nodes): 333 self.dispatch(n) 334 self._make_constant(i) # for __getitem__(i) at run-time 335 return None 336 337 def visitAssName(self, node): 338 self.store(node.name, self.expr) 339 return None 340 341 visitAssTuple = visitAssList 342 343 visitAugAssign = OP 344 345 visitBackquote = OP 346 347 visitBitand = OP 348 349 visitBitor = OP 350 351 visitBitxor = OP 352 353 visitBreak = NOP 354 355 visitCallFunc = OP 356 357 def visitClass(self, node): 358 359 """ 360 Register the class at the given 'node' subject to the restrictions 361 mentioned in the module docstring. 362 """ 363 364 if self.namespaces: 365 print "Class %r in %r is not global: ignored." % (node.name, self.namespaces[-1].full_name()) 366 return None 367 else: 368 cls = Class(node.name, self.get_parent(), self, node) 369 370 # Visit the base class expressions, attempting to find concrete 371 # definitions of classes. 372 373 for base in node.bases: 374 expr = self.dispatch(base) 375 if isinstance(expr, Attr): 376 if expr.assignments != 1: 377 raise InspectError(self.full_name(), node, 378 "Base class %r for %r is not constant." % (base, cls.full_name())) 379 else: 380 cls.add_base(expr.value) 381 else: # if expr is None: 382 raise InspectError(self.full_name(), node, 383 "Base class %r for %r is not found: it may be hidden in some way." % (base, cls.full_name())) 384 385 # NOTE: Potentially dubious measure to permit __init__ availability. 386 # If no bases exist, adopt the 'object' class. 387 388 if not node.bases and not (self.name == "__builtins__" and node.name == "object") : 389 expr = self.dispatch(compiler.ast.Name("object")) 390 cls.add_base(expr.value) 391 392 # Make a back reference from the node for code generation. 393 394 node.unit = cls 395 396 # Make an entry for the class. 397 398 self.store(node.name, cls) 399 400 # Process the class body. 401 402 self.namespaces.append(cls) 403 self.dispatch(node.code) 404 self.namespaces.pop() 405 406 return cls 407 408 visitCompare = OP 409 410 def visitConst(self, node): 411 return self._make_constant(node.value) 412 413 visitContinue = NOP 414 415 visitDecorators = NOP 416 417 visitDict = OP 418 419 visitDiscard = NOP 420 421 visitDiv = OP 422 423 visitEllipsis = NOP 424 425 visitExec = NOP 426 427 visitExpression = OP 428 429 visitFloorDiv = OP 430 431 def visitFor(self, node): 432 self.in_loop = 1 433 self.NOP(node) 434 self.in_loop = 0 435 return None 436 437 def visitFrom(self, node): 438 module = self.importer.load(node.modname, 1) 439 440 #if module is None: 441 # print "Warning:", node.modname, "not imported." 442 443 for name, alias in node.names: 444 if name != "*": 445 if module is not None and module.namespace.has_key(name): 446 attr = module[name] 447 self.store(alias or name, attr.value) 448 if isinstance(attr, Module) and not attr.loaded: 449 self.importer.load(attr.name) 450 451 # Support the import of names from missing modules. 452 453 else: 454 self.store(alias or name, UnresolvedName(name, node.modname, self)) 455 else: 456 if module is not None: 457 for n in module.namespace.keys(): 458 attr = module[n] 459 self.store(n, attr.value) 460 if isinstance(attr, Module) and not attr.loaded: 461 self.importer.load(attr.name) 462 463 return None 464 465 def visitFunction(self, node): 466 return self._visitFunction(node, node.name) 467 468 visitGenExpr = OP 469 470 visitGenExprFor = NOP 471 472 visitGenExprIf = NOP 473 474 visitGenExprInner = NOP 475 476 def visitGetattr(self, node): 477 expr = self.dispatch(node.expr) 478 if isinstance(expr, Attr): 479 value = expr.value 480 if isinstance(value, (Class, Module)): 481 return value.namespace.get(node.attrname) 482 elif isinstance(value, UnresolvedName): 483 return UnresolvedName(node.attrname, value.full_name(), self) 484 else: 485 return None 486 if self.builtins is not None: 487 return self.builtins.get(node.attrname) 488 else: 489 return UnresolvedName(node.attrname, value.full_name(), self) 490 491 def visitGlobal(self, node): 492 if self.namespaces: 493 for name in node.names: 494 self.namespaces[-1].make_global(name) 495 496 # Record a global entry for the name in the module. 497 498 if not self.has_key(name): 499 self[name] = Global() 500 501 def visitIf(self, node): 502 for test, body in node.tests: 503 self.dispatch(body) 504 if node.else_ is not None: 505 self.dispatch(node.else_) 506 return None 507 508 visitIfExp = NOP 509 510 def visitImport(self, node): 511 for name, alias in node.names: 512 if alias is not None: 513 self.store(alias, self.importer.load(name, 1) or UnresolvedName(None, name, self)) 514 else: 515 self.store(name.split(".")[0], self.importer.load(name) or UnresolvedName(None, name.split(".")[0], self)) 516 517 return None 518 519 visitInvert = OP 520 521 def visitKeyword(self, node): 522 self.dispatch(node.expr) 523 self._make_constant(node.name) 524 self.keyword_names.add(node.name) 525 return None 526 527 def visitLambda(self, node): 528 return self._visitFunction(node, None) 529 530 visitLeftShift = OP 531 532 visitList = OP 533 534 visitListComp = OP 535 536 visitListCompFor = NOP 537 538 visitListCompIf = NOP 539 540 visitMod = OP 541 542 def visitModule(self, node): 543 544 # Make a back reference from the node for code generation. 545 546 node.unit = self 547 return self.dispatch(node.node) 548 549 visitMul = OP 550 551 def visitName(self, node): 552 name = node.name 553 if name == "None": 554 return self._make_constant(None) 555 elif self.namespaces and self.namespaces[-1].has_key(name): 556 return self.namespaces[-1][name] 557 elif self.has_key(name): 558 return self[name] 559 elif self.builtins is not None and self.builtins.has_key(name): 560 return self.builtins[name] 561 else: 562 return None 563 564 visitNot = OP 565 566 visitOr = OP 567 568 visitPass = NOP 569 570 visitPower = OP 571 572 visitPrint = NOP 573 574 visitPrintnl = NOP 575 576 visitRaise = NOP 577 578 visitReturn = NOP 579 580 visitRightShift = OP 581 582 visitSlice = OP 583 584 visitSliceobj = OP 585 586 def visitStmt(self, node): 587 for n in node.nodes: 588 self.dispatch(n) 589 return None 590 591 visitSub = OP 592 593 visitSubscript = OP 594 595 def visitTryExcept(self, node): 596 self.dispatch(node.body) 597 for name, var, n in node.handlers: 598 599 # Establish the local for the handler. 600 601 if var is not None: 602 self.dispatch(var) 603 if n is not None: 604 self.dispatch(n) 605 if node.else_ is not None: 606 self.dispatch(node.else_) 607 return None 608 609 visitTryFinally = NOP 610 611 visitTuple = OP 612 613 visitUnaryAdd = OP 614 615 visitUnarySub = OP 616 617 def visitWhile(self, node): 618 self.in_loop = 1 619 self.NOP(node) 620 self.in_loop = 0 621 return None 622 623 visitWith = NOP 624 625 visitYield = NOP 626 627 class Global: 628 629 """ 630 A reference to an object assigned to a global from outside the module 631 top-level. 632 """ 633 634 pass 635 636 # vim: tabstop=4 expandtab shiftwidth=4