| | 42 | def resolveDottedName(self, dottedname, verbose=False): |
|---|
| | 43 | parts = dottedname.split('.') |
|---|
| | 44 | obj = self |
|---|
| | 45 | system = self.system |
|---|
| | 46 | while parts[0] not in obj._name2fullname: |
|---|
| | 47 | obj = obj.parent |
|---|
| | 48 | if obj is None: |
|---|
| | 49 | if parts[0] in system.allobjects: |
|---|
| | 50 | obj = system.allobjects[parts[0]] |
|---|
| | 51 | break |
|---|
| | 52 | if verbose: |
|---|
| | 53 | print "1 didn't find %r from %r"%(dottedname, |
|---|
| | 54 | self.fullName()) |
|---|
| | 55 | return None |
|---|
| | 56 | else: |
|---|
| | 57 | fn = obj._name2fullname[parts[0]] |
|---|
| | 58 | if fn in system.allobjects: |
|---|
| | 59 | obj = system.allobjects[fn] |
|---|
| | 60 | else: |
|---|
| | 61 | if verbose: |
|---|
| | 62 | print "1.5 didn't find %r from %r"%(dottedname, |
|---|
| | 63 | self.fullName()) |
|---|
| | 64 | return None |
|---|
| | 65 | for p in parts[1:]: |
|---|
| | 66 | if p not in obj.contents: |
|---|
| | 67 | if verbose: |
|---|
| | 68 | print "2 didn't find %r from %r"%(dottedname, |
|---|
| | 69 | self.fullName()) |
|---|
| | 70 | return None |
|---|
| | 71 | obj = obj.contents[p] |
|---|
| | 72 | if verbose: |
|---|
| | 73 | print dottedname, '->', obj.fullName(), 'in', self.fullName() |
|---|
| | 74 | return obj |
|---|
| | 75 | |
|---|
| | 76 | def dottedNameToFullName(self, dottedname): |
|---|
| | 77 | if '.' not in dottedname: |
|---|
| | 78 | start, rest = dottedname, '' |
|---|
| | 79 | else: |
|---|
| | 80 | start, rest = dottedname.split('.', 1) |
|---|
| | 81 | rest = '.' + rest |
|---|
| | 82 | obj = self |
|---|
| | 83 | while start not in obj._name2fullname: |
|---|
| | 84 | obj = obj.parent |
|---|
| | 85 | if obj is None: |
|---|
| | 86 | return dottedname |
|---|
| | 87 | return obj._name2fullname[start] + rest |
|---|
| | 88 | |
|---|
| | 89 | def __getstate__(self): |
|---|
| | 90 | # this is so very, very evil. |
|---|
| | 91 | # see doc/extreme-pickling-pain.txt for more. |
|---|
| | 92 | r = {} |
|---|
| | 93 | for k, v in self.__dict__.iteritems(): |
|---|
| | 94 | if isinstance(v, Documentable): |
|---|
| | 95 | r['$'+k] = v.fullName() |
|---|
| | 96 | elif isinstance(v, list) and v: |
|---|
| | 97 | for vv in v: |
|---|
| | 98 | if vv is not None and not isinstance(vv, Documentable): |
|---|
| | 99 | r[k] = v |
|---|
| | 100 | break |
|---|
| | 101 | else: |
|---|
| | 102 | rr = [] |
|---|
| | 103 | for vv in v: |
|---|
| | 104 | if vv is None: |
|---|
| | 105 | rr.append(vv) |
|---|
| | 106 | else: |
|---|
| | 107 | rr.append(vv.fullName()) |
|---|
| | 108 | r['@'+k] = rr |
|---|
| | 109 | elif isinstance(v, dict) and v: |
|---|
| | 110 | for vv in v.itervalues(): |
|---|
| | 111 | if not isinstance(vv, Documentable): |
|---|
| | 112 | r[k] = v |
|---|
| | 113 | break |
|---|
| | 114 | else: |
|---|
| | 115 | rr = {} |
|---|
| | 116 | for kk, vv in v.iteritems(): |
|---|
| | 117 | rr[kk] = vv.fullName() |
|---|
| | 118 | r['!'+k] = rr |
|---|
| | 119 | else: |
|---|
| | 120 | r[k] = v |
|---|
| | 121 | return r |
|---|
| 71 | | pass |
|---|
| | 152 | kind = "Function" |
|---|
| | 153 | |
|---|
| | 154 | |
|---|
| | 155 | class ModuleVistor(object): |
|---|
| | 156 | def __init__(self, system, modname): |
|---|
| | 157 | self.system = system |
|---|
| | 158 | self.modname = modname |
|---|
| | 159 | self.morenodes = [] |
|---|
| | 160 | |
|---|
| | 161 | def default(self, node): |
|---|
| | 162 | for child in node.getChildNodes(): |
|---|
| | 163 | self.visit(child) |
|---|
| | 164 | |
|---|
| | 165 | def postpone(self, docable, node): |
|---|
| | 166 | self.morenodes.append((docable, node)) |
|---|
| | 167 | |
|---|
| | 168 | def visitModule(self, node): |
|---|
| | 169 | if self.system.current and self.modname in self.system.current.contents: |
|---|
| | 170 | m = self.system.current.contents[self.modname] |
|---|
| | 171 | assert m.docstring is None |
|---|
| | 172 | m.docstring = node.doc |
|---|
| | 173 | self.system.push(m) |
|---|
| | 174 | self.default(node) |
|---|
| | 175 | self.system.pop(m) |
|---|
| | 176 | else: |
|---|
| | 177 | if not self.system.current: |
|---|
| | 178 | roots = [x for x in self.system.rootobjects if x.name == self.modname] |
|---|
| | 179 | if roots: |
|---|
| | 180 | mod, = roots |
|---|
| | 181 | self.system.push(mod) |
|---|
| | 182 | self.default(node) |
|---|
| | 183 | self.system.pop(mod) |
|---|
| | 184 | return |
|---|
| | 185 | self.system.pushModule(self.modname, node.doc) |
|---|
| | 186 | self.default(node) |
|---|
| | 187 | self.system.popModule() |
|---|
| | 188 | |
|---|
| | 189 | def visitClass(self, node): |
|---|
| | 190 | cls = self.system.pushClass(node.name, node.doc) |
|---|
| | 191 | if node.lineno is not None: |
|---|
| | 192 | cls.linenumber = node.lineno |
|---|
| | 193 | for n in node.bases: |
|---|
| | 194 | str_base = ast_pp.pp(n) |
|---|
| | 195 | cls.rawbases.append(str_base) |
|---|
| | 196 | base = cls.dottedNameToFullName(str_base) |
|---|
| | 197 | cls.bases.append(base) |
|---|
| | 198 | self.default(node) |
|---|
| | 199 | self.system.popClass() |
|---|
| | 200 | |
|---|
| | 201 | def visitFrom(self, node): |
|---|
| | 202 | modname = expandModname(self.system, node.modname) |
|---|
| | 203 | name2fullname = self.system.current._name2fullname |
|---|
| | 204 | for fromname, asname in node.names: |
|---|
| | 205 | if fromname == '*': |
|---|
| | 206 | self.system.warning("import *", modname) |
|---|
| | 207 | if modname not in self.system.allobjects: |
|---|
| | 208 | return |
|---|
| | 209 | mod = self.system.allobjects[modname] |
|---|
| | 210 | # this might fail if you have an import-* cycle, or if |
|---|
| | 211 | # you're just not running the import star finder to |
|---|
| | 212 | # save time (not that this is possibly without |
|---|
| | 213 | # commenting stuff out yet, but...) |
|---|
| | 214 | if isinstance(mod, Package): |
|---|
| | 215 | self.system.warning("import * from a package", modname) |
|---|
| | 216 | return |
|---|
| | 217 | if mod.processed: |
|---|
| | 218 | for n in mod.contents: |
|---|
| | 219 | name2fullname[n] = modname + '.' + n |
|---|
| | 220 | else: |
|---|
| | 221 | self.system.warning("unresolvable import *", modname) |
|---|
| | 222 | return |
|---|
| | 223 | if asname is None: |
|---|
| | 224 | asname = fromname |
|---|
| | 225 | name2fullname[asname] = modname + '.' + fromname |
|---|
| | 226 | |
|---|
| | 227 | def visitImport(self, node): |
|---|
| | 228 | name2fullname = self.system.current._name2fullname |
|---|
| | 229 | for fromname, asname in node.names: |
|---|
| | 230 | fullname = expandModname(self.system, fromname) |
|---|
| | 231 | if asname is None: |
|---|
| | 232 | asname = fromname.split('.', 1)[0] |
|---|
| | 233 | # aaaaargh! python sucks. |
|---|
| | 234 | parts = fullname.split('.') |
|---|
| | 235 | for i, part in enumerate(fullname.split('.')[::-1]): |
|---|
| | 236 | if part == asname: |
|---|
| | 237 | fullname = '.'.join(parts[:len(parts)-i]) |
|---|
| | 238 | name2fullname[asname] = fullname |
|---|
| | 239 | break |
|---|
| | 240 | else: |
|---|
| | 241 | name2fullname[asname] = '.'.join(parts) |
|---|
| | 242 | else: |
|---|
| | 243 | name2fullname[asname] = fullname |
|---|
| | 244 | |
|---|
| | 245 | def visitFunction(self, node): |
|---|
| | 246 | func = self.system.pushFunction(node.name, node.doc) |
|---|
| | 247 | if node.lineno is not None: |
|---|
| | 248 | func.linenumber = node.lineno |
|---|
| | 249 | # ast.Function has a pretty lame representation of |
|---|
| | 250 | # arguments. Let's convert it to a nice concise format |
|---|
| | 251 | # somewhat like what inspect.getargspec returns |
|---|
| | 252 | argnames = node.argnames[:] |
|---|
| | 253 | kwname = starargname = None |
|---|
| | 254 | if node.kwargs: |
|---|
| | 255 | kwname = argnames.pop(-1) |
|---|
| | 256 | if node.varargs: |
|---|
| | 257 | starargname = argnames.pop(-1) |
|---|
| | 258 | defaults = [] |
|---|
| | 259 | for default in node.defaults: |
|---|
| | 260 | try: |
|---|
| | 261 | defaults.append(ast_pp.pp(default)) |
|---|
| | 262 | except (KeyboardInterrupt, SystemExit): |
|---|
| | 263 | raise |
|---|
| | 264 | except Exception, e: |
|---|
| | 265 | self.system.warning("unparseable default", "%s: %s %r"%(e.__class__.__name__, |
|---|
| | 266 | e, default)) |
|---|
| | 267 | defaults.append('???') |
|---|
| | 268 | # argh, convert unpacked-arguments from tuples to lists, |
|---|
| | 269 | # because that's what getargspec uses and the unit test |
|---|
| | 270 | # compares it |
|---|
| | 271 | argnames2 = [] |
|---|
| | 272 | for argname in argnames: |
|---|
| | 273 | if isinstance(argname, tuple): |
|---|
| | 274 | argname = list(argname) |
|---|
| | 275 | argnames2.append(argname) |
|---|
| | 276 | func.argspec = (argnames2, starargname, kwname, tuple(defaults)) |
|---|
| | 277 | self.postpone(func, node.code) |
|---|
| | 278 | self.system.popFunction() |
|---|
| | 279 | |
|---|
| | 280 | states = [ |
|---|
| | 281 | 'blank', |
|---|
| | 282 | 'preparse', |
|---|
| | 283 | 'importstarred', |
|---|
| | 284 | 'parsed', |
|---|
| | 285 | 'finalized', |
|---|
| | 286 | ] |
|---|
| 244 | | modname, []).append(self.modfullname) |
|---|
| 245 | | |
|---|
| 246 | | class ModuleVistor(object): |
|---|
| 247 | | def __init__(self, system, modname): |
|---|
| 248 | | self.system = system |
|---|
| 249 | | self.modname = modname |
|---|
| 250 | | self.morenodes = [] |
|---|
| 251 | | |
|---|
| 252 | | def default(self, node): |
|---|
| 253 | | for child in node.getChildNodes(): |
|---|
| 254 | | self.visit(child) |
|---|
| 255 | | |
|---|
| 256 | | def postpone(self, docable, node): |
|---|
| 257 | | self.morenodes.append((docable, node)) |
|---|
| 258 | | |
|---|
| 259 | | def visitModule(self, node): |
|---|
| 260 | | if self.system.current and self.modname in self.system.current.contents: |
|---|
| 261 | | m = self.system.current.contents[self.modname] |
|---|
| 262 | | assert m.docstring is None |
|---|
| 263 | | m.docstring = node.doc |
|---|
| 264 | | self.system.push(m, node) |
|---|
| 265 | | self.default(node) |
|---|
| 266 | | self.system.pop(m) |
|---|
| 267 | | else: |
|---|
| 268 | | self.system.pushModule(self.modname, node.doc) |
|---|
| 269 | | self.default(node) |
|---|
| 270 | | self.system.popModule() |
|---|
| 271 | | |
|---|
| 272 | | def visitClass(self, node): |
|---|
| 273 | | cls = self.system.pushClass(node.name, node.doc) |
|---|
| 274 | | for n in node.bases: |
|---|
| 275 | | if isinstance(n, ast.Name): |
|---|
| 276 | | cls.bases.append(cls.parent.name2fullname(n.name)) |
|---|
| 277 | | elif isinstance(n, ast.Getattr): |
|---|
| 278 | | p = [] |
|---|
| 279 | | while isinstance(n, ast.Getattr): |
|---|
| 280 | | p.append(n.attrname) |
|---|
| 281 | | n = n.expr |
|---|
| 282 | | assert isinstance(n, ast.Name) |
|---|
| 283 | | p.append(cls.parent.name2fullname(n.name)) |
|---|
| 284 | | p.reverse() |
|---|
| 285 | | assert None not in p, n |
|---|
| 286 | | cls.bases.append('.'.join(p)) |
|---|
| 287 | | else: |
|---|
| 288 | | assert not n |
|---|
| 289 | | self.default(node) |
|---|
| 290 | | self.system.popClass() |
|---|
| 291 | | |
|---|
| 292 | | def visitFrom(self, node): |
|---|
| 293 | | modname = expandModname(self.system, node.modname) |
|---|
| 294 | | name2fullname = self.system.current._name2fullname |
|---|
| 295 | | for fromname, asname in node.names: |
|---|
| 296 | | if fromname == '*': |
|---|
| 297 | | self.system.warning("import *", modname) |
|---|
| 298 | | if modname not in self.system.allobjects: |
|---|
| 299 | | return |
|---|
| 300 | | mod = self.system.allobjects[modname] |
|---|
| 301 | | #snarl (see below) |
|---|
| 302 | | #assert mod.processed |
|---|
| 303 | | self.system.warning("mwh is an idiot", "") |
|---|
| 304 | | for n in mod.contents: |
|---|
| 305 | | name2fullname[n] = modname + '.' + n |
|---|
| 306 | | return |
|---|
| 307 | | if asname is None: |
|---|
| 308 | | asname = fromname |
|---|
| 309 | | name2fullname[asname] = modname + '.' + fromname |
|---|
| 310 | | |
|---|
| 311 | | def visitImport(self, node): |
|---|
| 312 | | name2fullname = self.system.current._name2fullname |
|---|
| 313 | | for fromname, asname in node.names: |
|---|
| 314 | | fullname = expandModname(self.system, fromname) |
|---|
| 315 | | if asname is None: |
|---|
| 316 | | asname = fromname.split('.', 1)[0] |
|---|
| 317 | | # aaaaargh! python sucks. |
|---|
| 318 | | parts = fullname.split('.') |
|---|
| 319 | | for i, part in enumerate(fullname.split('.')[::-1]): |
|---|
| 320 | | if part == asname: |
|---|
| 321 | | fullname = '.'.join(parts[:len(parts)-i]) |
|---|
| 322 | | name2fullname[asname] = fullname |
|---|
| 323 | | break |
|---|
| 324 | | else: |
|---|
| 325 | | name2fullname[asname] = '.'.join(parts) |
|---|
| 326 | | else: |
|---|
| 327 | | name2fullname[asname] = fullname |
|---|
| 328 | | |
|---|
| 329 | | |
|---|
| 330 | | def visitFunction(self, node): |
|---|
| 331 | | fc = {} |
|---|
| 332 | | get_function_calls(node, fc) |
|---|
| 333 | | #print fc.keys() |
|---|
| 334 | | func = self.system.pushFunction(node.name, node.doc, fc) |
|---|
| 335 | | # ast.Function has a pretty lame representation of |
|---|
| 336 | | # arguments. Let's convert it to a nice concise |
|---|
| 337 | | # getargspec-like format and include it in the Function |
|---|
| 338 | | # object. |
|---|
| 339 | | argnames = node.argnames[:] |
|---|
| 340 | | kwname = starargname = None |
|---|
| 341 | | if node.kwargs: |
|---|
| 342 | | kwname = argnames.pop(-1) |
|---|
| 343 | | if node.varargs: |
|---|
| 344 | | starargname = argnames.pop(-1) |
|---|
| 345 | | defaults = [] |
|---|
| 346 | | for default in node.defaults: |
|---|
| 347 | | if isinstance(default, ast.Const): |
|---|
| 348 | | defaults.append(default.value) |
|---|
| 349 | | elif isinstance(default, ast.Name): |
|---|
| 350 | | defaults.append(default.name) |
|---|
| 351 | | else: |
|---|
| 352 | | self.system.warning("unparseable default", repr(default)) |
|---|
| 353 | | defaults.append('???') |
|---|
| 354 | | #assert False, "don't know how to handle default %r"%(default,) |
|---|
| 355 | | # argh, convert unpacked-arguments from tuples to lists, |
|---|
| 356 | | # because that's what getargspec uses and the unit test |
|---|
| 357 | | # compares it |
|---|
| 358 | | argnames2 = [] |
|---|
| 359 | | for argname in argnames: |
|---|
| 360 | | if isinstance(argname, tuple): |
|---|
| 361 | | argname = list(argname) |
|---|
| 362 | | argnames2.append(argname) |
|---|
| 363 | | func.argspec = (argnames2, starargname, kwname, tuple(defaults)) |
|---|
| 364 | | #for child in node.getChildren(): |
|---|
| 365 | | # if isinstance(child, compiler.ast.Stmt): |
|---|
| 366 | | # for c in child.getChildren(): |
|---|
| 367 | | # print c.__class__ |
|---|
| 368 | | # print c |
|---|
| 369 | | self.postpone(func, node.code) |
|---|
| 370 | | self.system.popFunction() |
|---|
| 371 | | |
|---|
| 372 | | def get_function_calls(node, fc): |
|---|
| 373 | | if not isinstance(node, compiler.ast.Node): |
|---|
| 374 | | return |
|---|
| 375 | | for child in node.getChildren(): |
|---|
| 376 | | #print "child:", child |
|---|
| 377 | | if isinstance(child, compiler.ast.CallFunc): |
|---|
| 378 | | funcname = "" |
|---|
| 379 | | attrname = "" |
|---|
| 380 | | n = child.node |
|---|
| 381 | | #print "n:", n |
|---|
| 382 | | #print n.__class__ |
|---|
| 383 | | if isinstance(n, compiler.ast.Getattr): |
|---|
| 384 | | expr = n.expr |
|---|
| 385 | | if isinstance(expr, compiler.ast.Name): |
|---|
| 386 | | funcname = expr.name |
|---|
| 387 | | attrname = n.attrname |
|---|
| 388 | | func_called = "" |
|---|
| 389 | | if funcname: func_called = funcname + "." |
|---|
| 390 | | func_called += attrname |
|---|
| 391 | | if func_called: |
|---|
| 392 | | fc[func_called] = 1 |
|---|
| 393 | | get_function_calls(child, fc) |
|---|
| 394 | | |
|---|
| | 507 | self.modfullname, []).append(modname) |
|---|
| | 508 | |
|---|