1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/micropython/inspect.py Tue Oct 16 01:29:33 2007 +0200
1.3 @@ -0,0 +1,273 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +Inspect source files, obtaining details of classes and attributes.
1.8 +
1.9 +Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk>
1.10 +
1.11 +This program is free software; you can redistribute it and/or modify it under
1.12 +the terms of the GNU General Public License as published by the Free Software
1.13 +Foundation; either version 3 of the License, or (at your option) any later
1.14 +version.
1.15 +
1.16 +This program is distributed in the hope that it will be useful, but WITHOUT
1.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.19 +details.
1.20 +
1.21 +You should have received a copy of the GNU General Public License along with
1.22 +this program. If not, see <http://www.gnu.org/licenses/>.
1.23 +"""
1.24 +
1.25 +import compiler.ast
1.26 +from compiler.visitor import ASTVisitor
1.27 +try:
1.28 + set
1.29 +except NameError:
1.30 + from sets import Set as set
1.31 +
1.32 +class InspectError(Exception):
1.33 +
1.34 + "An inspection error."
1.35 +
1.36 + pass
1.37 +
1.38 +class Class:
1.39 +
1.40 + "An inspected class."
1.41 +
1.42 + def __init__(self, name):
1.43 + self.name = name
1.44 + self.bases = []
1.45 + self.namespace = {}
1.46 + self.instattr = set()
1.47 +
1.48 + def add_base(self, base):
1.49 + self.bases.append(base)
1.50 +
1.51 +class Function:
1.52 +
1.53 + "An inspected function."
1.54 +
1.55 + def __init__(self, name):
1.56 + self.name = name
1.57 +
1.58 +class Module(ASTVisitor):
1.59 +
1.60 + "An inspected module."
1.61 +
1.62 + def __init__(self, importer=None):
1.63 + ASTVisitor.__init__(self)
1.64 + self.visitor = self
1.65 +
1.66 + self.importer = importer
1.67 +
1.68 + self.namespace = {}
1.69 + self.namespace.update(builtins_namespace)
1.70 +
1.71 + self.in_global = 1
1.72 + self.in_init = 0
1.73 + self.expr = None
1.74 +
1.75 + self.classes = []
1.76 + self.module = None
1.77 +
1.78 + def parse(self, filename):
1.79 + module = compiler.parseFile(filename)
1.80 + self.process(module)
1.81 +
1.82 + def process(self, module):
1.83 + self.module = module
1.84 + return self.dispatch(module)
1.85 +
1.86 + def default(self, node, *args):
1.87 + raise InspectError, (None, node)
1.88 +
1.89 + def dispatch(self, node, *args):
1.90 + return ASTVisitor.dispatch(self, node, *args)
1.91 +
1.92 + def store(self, name, obj):
1.93 + if self.in_global:
1.94 + if not self.classes:
1.95 + self.namespace[name] = obj
1.96 + else:
1.97 + self.classes[-1].namespace[name] = obj
1.98 +
1.99 + def store_attr(self, name):
1.100 + if self.in_init:
1.101 + self.classes[-1].instattr.add(name)
1.102 +
1.103 + def NOP(self, node):
1.104 + return node
1.105 +
1.106 + visitAnd = NOP
1.107 +
1.108 + def visitAssign(self, node):
1.109 + self.expr = self.dispatch(node.expr)
1.110 + for n in node.nodes:
1.111 + self.dispatch(n)
1.112 + return None
1.113 +
1.114 + def visitAssAttr(self, node):
1.115 + expr = self.dispatch(node.expr)
1.116 + if expr is not None and isinstance(expr, Self):
1.117 + self.store_attr(node.attrname)
1.118 + return None
1.119 +
1.120 + def visitAssList(self, node):
1.121 + for n in node.nodes:
1.122 + self.dispatch(n)
1.123 + return None
1.124 +
1.125 + def visitAssName(self, node):
1.126 + self.store(node.name, self.expr)
1.127 + return None
1.128 +
1.129 + visitAssTuple = visitAssList
1.130 +
1.131 + visitCallFunc = NOP
1.132 +
1.133 + def visitClass(self, node):
1.134 + if not self.in_global:
1.135 + raise InspectError, "Class is %s not global: cannot handle this reasonably." % node.name
1.136 + else:
1.137 + cls = Class(node)
1.138 + for base in node.bases:
1.139 + base_ref = self.dispatch(base)
1.140 + if base_ref is None:
1.141 + raise InspectError, "Base class %s for class %s is not found: it may be hidden in some way." % (base, node.name)
1.142 + cls.add_base(base_ref)
1.143 +
1.144 + # Make an entry for the class.
1.145 +
1.146 + self.store(node.name, cls)
1.147 +
1.148 + self.classes.append(cls)
1.149 + self.dispatch(node.code)
1.150 + self.classes.pop()
1.151 +
1.152 + return node
1.153 +
1.154 + visitConst = NOP
1.155 +
1.156 + visitDict = NOP
1.157 +
1.158 + visitDiscard = NOP
1.159 +
1.160 + visitFor = NOP
1.161 +
1.162 + def visitFrom(self, node):
1.163 + if self.importer is None:
1.164 + raise InspectError, "Please use the micropython.Importer class for code which uses the 'from' statement."
1.165 +
1.166 + module = self.importer.load(node.modname, 1)
1.167 +
1.168 + if module is None:
1.169 + print "Warning:", node.modname, "not imported."
1.170 + return None
1.171 +
1.172 + for name, alias in node.names:
1.173 + if name != "*":
1.174 + self.namespace[alias] = module.namespace[name]
1.175 + else:
1.176 + for n in module.namespace.keys():
1.177 + self.namespace[n] = module.namespace[n]
1.178 +
1.179 + return None
1.180 +
1.181 + def visitFunction(self, node):
1.182 + self.store(node.name, Function(node.name))
1.183 +
1.184 + in_global = self.in_global
1.185 + self.in_global = 0
1.186 + if node.name == "__init__" and self.classes:
1.187 + self.in_init = 1
1.188 + self.dispatch(node.code)
1.189 + self.in_init = 0
1.190 + self.in_global = in_global
1.191 + return None
1.192 +
1.193 + def visitGetattr(self, node):
1.194 + expr = self.dispatch(node.expr)
1.195 + if expr is not None and isinstance(expr, Module):
1.196 + return expr.namespace.get(node.attrname)
1.197 + else:
1.198 + return None
1.199 +
1.200 + def visitIf(self, node):
1.201 + for test, body in node.tests:
1.202 + self.dispatch(body)
1.203 + if node.else_ is not None:
1.204 + self.dispatch(node.else_)
1.205 + return None
1.206 +
1.207 + def visitImport(self, node):
1.208 + if self.importer is None:
1.209 + raise InspectError, "Please use the micropython.Importer class for code which uses the 'import' statement."
1.210 +
1.211 + for name, alias in node.names:
1.212 + self.namespace[alias] = self.importer.load(name, 1)
1.213 +
1.214 + return None
1.215 +
1.216 + visitList = NOP
1.217 +
1.218 + def visitModule(self, node):
1.219 + return self.dispatch(node.node)
1.220 +
1.221 + def visitName(self, node):
1.222 + name = node.name
1.223 + if name == "self":
1.224 + return Self()
1.225 + elif self.namespace.has_key(name):
1.226 + return self.namespace[name]
1.227 + else:
1.228 + return None
1.229 +
1.230 + visitNot = NOP
1.231 +
1.232 + visitOr = NOP
1.233 +
1.234 + visitPass = NOP
1.235 +
1.236 + visitRaise = NOP
1.237 +
1.238 + def visitStmt(self, node):
1.239 + for n in node.nodes:
1.240 + self.dispatch(n)
1.241 + return None
1.242 +
1.243 + visitSubscript = NOP
1.244 +
1.245 + def visitTryExcept(self, node):
1.246 + self.dispatch(node.body)
1.247 + for name, var, n in node.handlers:
1.248 + self.dispatch(n)
1.249 + if node.else_ is not None:
1.250 + self.dispatch(node.else_)
1.251 + return None
1.252 +
1.253 +class Self:
1.254 +
1.255 + "A reference to an object within a method."
1.256 +
1.257 + pass
1.258 +
1.259 +builtins_namespace = {}
1.260 +for key in ['ArithmeticError', 'AssertionError', 'AttributeError',
1.261 + 'BaseException', 'DeprecationWarning', 'EOFError', 'Ellipsis',
1.262 + 'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
1.263 + 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
1.264 + 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError',
1.265 + 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError',
1.266 + 'None', 'NotImplemented', 'NotImplementedError', 'OSError',
1.267 + 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError',
1.268 + 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration',
1.269 + 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit',
1.270 + 'TabError', 'True', 'TypeError', 'UnboundLocalError',
1.271 + 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError',
1.272 + 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
1.273 + 'ValueError', 'Warning', 'ZeroDivisionError']:
1.274 + builtins_namespace[key] = Class(key)
1.275 +
1.276 +# vim: tabstop=4 expandtab shiftwidth=4