1 """Utilities for pysource
8 # We'd better have at least *one* module in this package that demonstrates
9 # *not* using reST for our docstrings...
10 __docformat__
= "none"
13 # ----------------------------------------------------------------------
14 PLAINTEXT
= "plaintext"
15 RESTRUCTUREDTEXT
= "restructuredtext"
17 canonical_format
= { "plaintext" : PLAINTEXT
,
20 "rst" : RESTRUCTUREDTEXT
,
21 "rest" : RESTRUCTUREDTEXT
,
22 "rtxt" : RESTRUCTUREDTEXT
,
23 "restructuredtext" : RESTRUCTUREDTEXT
,
27 """Interpret a module's __docformat__ string.
29 Returns a tuple of (format,language)
34 words
= string
.split(string
.lower(text
))
41 raise ValueError,"__docformat__ may be at most two 'words'"
44 language
= string
.lower(words
[1])
49 format
= canonical_format
[string
.lower(words
[0])]
51 legal
= canonical_format
.keys()
53 raise ValueError,"__docformat__ should be one of %s"%legal
55 return format
,language
58 # ----------------------------------------------------------------------
60 """Retrieve some simpler representation of our AST.
62 (and it's not meant to be 'theoretically' wonderful, just something
63 I can look at to work out how an AST works...)
65 if isinstance(item
,compiler
.ast
.Node
):
66 things
= [item
.__class
__.__name
__]
67 children
= item
.getChildren()
68 for child
in children
:
69 things
.append(flatten(child
))
75 # ----------------------------------------------------------------------
76 def treeprint(stream
,item
,indent
=0):
77 """Simple pretty printer for the AST."""
78 if isinstance(item
,compiler
.ast
.Node
):
79 stream
.write("\n%s<%s>"%(" "*indent
,item
.__class
__.__name
__))
81 children
= item
.getChildren()
82 for child
in children
:
83 treeprint(stream
,child
,indent
+2)
85 # Fake our docstring as a sub-node (it's *really* more an attribute)
86 if hasattr(item
,"docstring"):
87 stream
.write("\n%s <docstring> %s"%(" "*indent
,item
.docstring
))
89 # And ditto for a remembered assignment expression
90 if hasattr(item
,"assign_expr"):
91 stream
.write("\n%s <assign_expr>"%(" "*indent
))
92 treeprint(stream
,item
.assign_expr
,indent
+4)
98 # ----------------------------------------------------------------------
99 def find_attr_docs(tree
,verbose
=0):
100 """Find candidates for documented attributes
102 Note that after this, it may be that the AST will not garbage collect
103 its own nodes properly anymore, as we are adding in cross-linkages.
106 if not isinstance(tree
,compiler
.ast
.Node
):
109 children
= tree
.getChildren()
111 # Might as well get our recursion done with first...
112 for child
in children
:
113 find_attr_docs(child
,verbose
)
115 # I believe that only Stmt nodes can have Assign and Discard
117 if not isinstance(tree
,compiler
.ast
.Stmt
):
120 if len(children
) == 0:
125 for item
in children
[1:]:
126 pairs
.append((last
,item
))
129 for this
,next
in pairs
:
130 if isinstance(this
,compiler
.ast
.Assign
) and \
131 isinstance(next
,compiler
.ast
.Discard
):
135 print "*** Attribute docstring candidate"
141 if isinstance(nextexpr
,compiler
.ast
.Const
):
142 if type(nextexpr
.value
) == types
.StringType
:
143 docstring
= nextexpr
.value
147 print "... Discarded constant is not a string"
152 print "... Discarded expression is not a constant"
155 # If there is more than one assignment attached to
156 # the <Assign> node, we are not interested
157 if len(this
.nodes
) > 1:
160 print "... (but there are too many assignments in the <Assign>)"
163 target
= this
.nodes
[0]
164 if isinstance(target
,compiler
.ast
.AssName
):
165 # Let's be cheeky and glue the docstring on...
166 target
.docstring
= docstring
167 elif isinstance(target
,compiler
.ast
.AssAttr
):
168 # Let's be cheeky and glue the docstring on...
169 target
.docstring
= docstring
173 print "... (but the assignment is to a tuple/list/etc.)"
182 # ----------------------------------------------------------------------
183 def find_attr_vals(tree
,verbose
=0):
184 """Find attributes whose values we're interested in.
186 Clearly, when this is working, it could do with being "folded" into
189 Note that after this, it may be that the AST will not garbage collect
190 its own nodes properly anymore, as we are adding in cross-linkages.
193 if not isinstance(tree
,compiler
.ast
.Node
):
196 children
= tree
.getChildren()
198 # Might as well get our recursion done with first...
199 for child
in children
:
200 find_attr_vals(child
,verbose
)
202 # I believe that only Stmt nodes can have Assign and Discard
204 if not isinstance(tree
,compiler
.ast
.Stmt
):
207 for this
in children
:
208 if isinstance(this
,compiler
.ast
.Assign
):
212 print "*** Assignment - name/value candidate"
216 # If there is more than one assignment attached to
217 # the <Assign> node, we are not interested
218 if len(this
.nodes
) > 1:
221 print "... (but there are too many assignments in the <Assign>)"
224 target
= this
.nodes
[0]
225 if isinstance(target
,compiler
.ast
.AssName
) or \
226 isinstance(target
,compiler
.ast
.AssAttr
):
227 # Let's be cheeky and glue the associated expression on...
228 target
.assign_expr
= this
.expr
232 print "... (but the assignment is to a tuple/list/etc.)"
241 # ----------------------------------------------------------------------
242 def stringify_arg(thing
):
243 """Return a string representation of a function argument.
245 This just works for tuples of (strings or tuples (of strings ...) ...)
247 if type(thing
) == types
.StringType
:
249 elif type(thing
) == types
.TupleType
:
252 innards
.append(stringify_arg(item
))
253 return "(" + string
.join(innards
,",") + ")"
255 raise ValueError,"Tried to stringify type %s"%type(thing
)
258 # ----------------------------------------------------------------------
259 def merge_args(args
,defaults
):
260 """Merge together arguments and defaults from an argument list.
262 Returns a list of argument strings.
270 # This is horrible - do it nicely later on!
273 argstrs
.append(stringify_arg(item
))
275 pos
= len(args
) - len(defaults
)
277 for index
in range(pos
,len(args
)):
278 thing
= defaults
[next
]
279 thing
= stringify_expr(thing
)
280 argstrs
[index
] = "%s=%s"%(argstrs
[index
],thing
)
285 # ----------------------------------------------------------------------
286 def stringify_expr(thing
):
287 """Return a very simple string representation of an expression node
289 Specifically, this function aims to support stringifying things
290 which can be on the RHS of an assignment - is that *actually* the
291 same as stringifying expression nodes?
294 # Humph - saving typing may be a good thing...
295 strify
= stringify_expr
297 #print thing.__class__.__name__
301 elif isinstance(thing
,compiler
.ast
.Add
):
302 return strify(thing
.left
) + " + " + strify(thing
.right
)
303 elif isinstance(thing
,compiler
.ast
.And
):
305 for node
in thing
.nodes
:
306 exprs
.append(strify(node
))
307 return string
.join(exprs
," && ")
308 elif isinstance(thing
,compiler
.ast
.AssAttr
):
309 # Attribute as target of assignment
310 if thing
.flags
== compiler
.consts
.OP_ASSIGN
:
311 return strify(thing
.expr
) + "." + thing
.attrname
313 raise ValueError,"Unexpected flag %d in %s"%(thing
.flags
,`thing`
)
314 elif isinstance(thing
,compiler
.ast
.AssName
):
315 # Name as target of assignment, but this also means name
316 # as target of "in" assignment (e.g., "x in [1,2,3]"),
317 # which is why *we're* interested in it (since this can
318 # occur in list comprehensions, which can occur as the
319 # RHS of assignments)
320 if thing
.flags
== compiler
.consts
.OP_ASSIGN
:
323 raise ValueError,"Unexpected flag %d in %s"%(thing
.flags
,`thing`
)
324 elif isinstance(thing
,compiler
.ast
.Backquote
):
325 return "`" + strify(thing
.expr
) + "`"
326 elif isinstance(thing
,compiler
.ast
.Bitand
):
328 for node
in thing
.nodes
:
329 exprs
.append(strify(node
))
330 return string
.join(exprs
," & ")
331 elif isinstance(thing
,compiler
.ast
.Bitor
):
333 for node
in thing
.nodes
:
334 exprs
.append(strify(node
))
335 return string
.join(exprs
," | ")
336 elif isinstance(thing
,compiler
.ast
.Bitxor
):
338 for node
in thing
.nodes
:
339 exprs
.append(strify(node
))
340 return string
.join(exprs
," ^ ")
341 elif isinstance(thing
,compiler
.ast
.CallFunc
):
342 # Yuck - this is getting complicated!
343 # (for an example, see method `hyperlink_target` in
344 # restructuredtext/states.py)
345 str = strify(thing
.node
) + "("
348 for arg
in thing
.args
:
349 arglist
.append(strify(arg
))
351 arglist
.append("*"+strify(thing
.star_args
))
353 arglist
.append("**"+strify(thing
.dstar_args
))
355 str += string
.join(arglist
,", ")
357 elif isinstance(thing
,compiler
.ast
.Compare
):
358 str = strify(thing
.expr
) + " "
359 for op
,val
in thing
.ops
:
360 str += op
+ " " + strify(val
)
362 elif isinstance(thing
,compiler
.ast
.Const
):
363 # Try not to let long strings take up too much room...
365 if type(value
) == type("") and len(value
) > 50:
366 value
= value
[:47] + "..."
367 # Make Python decide for us if it needs quotes round it
368 # (and, if so, what sort)
370 elif isinstance(thing
,compiler
.ast
.Dict
):
372 for key
,val
in thing
.items
:
375 innards
.append(key
+":"+val
)
376 return "{" + string
.join(innards
,", ") + "}"
377 elif isinstance(thing
,compiler
.ast
.Div
):
378 return strify(thing
.left
) + " / " + strify(thing
.right
)
379 elif isinstance(thing
,compiler
.ast
.Ellipsis):
381 elif isinstance(thing
,compiler
.ast
.Getattr
):
382 return strify(thing
.expr
) + "." + thing
.attrname
383 elif isinstance(thing
,compiler
.ast
.Invert
):
385 return "~" + strify(thing
.expr
)
386 elif isinstance(thing
,compiler
.ast
.Keyword
):
387 # An 'arg=value' within a function call
388 return thing
.name
+ "=" + strify(thing
.expr
)
389 elif isinstance(thing
,compiler
.ast
.Lambda
):
392 str += " <flag %d> "%thing
.flags
393 str += string
.join(merge_args(thing
.argnames
,thing
.defaults
),", ")
395 str += strify(thing
.code
)
397 elif isinstance(thing
,compiler
.ast
.LeftShift
):
398 return strify(thing
.left
) + " << " + strify(thing
.right
)
399 elif isinstance(thing
,compiler
.ast
.List
):
401 for item
in thing
.nodes
:
402 innards
.append(strify(item
))
403 return "[" + string
.join(innards
,", ") + "]"
404 elif isinstance(thing
,compiler
.ast
.ListComp
):
405 str = "["+strify(thing
.expr
)
406 for node
in thing
.quals
:
407 str += " "+strify(node
)
409 elif isinstance(thing
,compiler
.ast
.ListCompFor
):
410 str = "for "+strify(thing
.assign
)
411 str += " in "+strify(thing
.list)
413 for node
in thing
.ifs
:
414 str += " "+strify(node
)
416 elif isinstance(thing
,compiler
.ast
.ListCompIf
):
417 return "if "+strify(thing
.test
)
418 elif isinstance(thing
,compiler
.ast
.Mod
):
419 return strify(thing
.left
) + "%" + strify(thing
.right
)
420 elif isinstance(thing
,compiler
.ast
.Mul
):
421 return strify(thing
.left
) + " * " + strify(thing
.right
)
422 elif isinstance(thing
,compiler
.ast
.Name
):
424 elif isinstance(thing
,compiler
.ast
.Not
):
425 return "not " + strify(thing
.expr
)
426 elif isinstance(thing
,compiler
.ast
.Or
):
428 for node
in thing
.nodes
:
429 exprs
.append(strify(node
))
430 return string
.join(exprs
," || ")
431 elif isinstance(thing
,compiler
.ast
.Power
):
432 return strify(thing
.left
) + " ** " + strify(thing
.right
)
433 elif isinstance(thing
,compiler
.ast
.RightShift
):
434 return strify(thing
.left
) + " >> " + strify(thing
.right
)
435 elif isinstance(thing
,compiler
.ast
.Slice
):
436 if thing
.flags
!= compiler
.consts
.OP_APPLY
:
437 raise ValueError,"Unexpected flag %d in %s"%(thing
.flags
,`thing`
)
438 return strify(thing
.expr
) + "[" + \
439 strify(thing
.lower
) + ":" + strify(thing
.upper
) + "]"
440 elif isinstance(thing
,compiler
.ast
.Sliceobj
):
442 for idx
in thing
.nodes
:
443 slicelist
.append(strify(idx
))
444 return string
.join(slicelist
,":")
445 elif isinstance(thing
,compiler
.ast
.Sub
):
446 return strify(thing
.left
) + " - " + strify(thing
.right
)
447 elif isinstance(thing
,compiler
.ast
.Subscript
):
448 if thing
.flags
!= compiler
.consts
.OP_APPLY
:
449 raise ValueError,"Unexpected flag %d in %s"%(thing
.flags
,`thing`
)
450 str = strify(thing
.expr
) + "["
452 for sub
in thing
.subs
:
453 sublist
.append(strify(sub
))
454 return str + string
.join(sublist
,", ") + "]"
455 elif isinstance(thing
,compiler
.ast
.Tuple
):
457 for item
in thing
.nodes
:
458 innards
.append(strify(item
))
459 return "(" + string
.join(innards
,", ") + ")"
460 elif isinstance(thing
,compiler
.ast
.UnaryAdd
):
461 return "+" + strify(thing
.expr
)
462 elif isinstance(thing
,compiler
.ast
.UnarySub
):
463 return "-" + strify(thing
.expr
)
465 return _whatsthis(thing
)
467 def _whatsthis(thing
):
468 # Wrong, but what else can we do?
470 print >>sys
.stderr
,"stringify_expr - don't recognise %s %s"%\
471 (thing
.__class
__.__name
__,thing
)