parser_utils.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import re
  2. import textwrap
  3. from inspect import cleandoc
  4. from parso.python import tree
  5. from parso.cache import parser_cache
  6. from jedi._compatibility import literal_eval, force_unicode
  7. _EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test',
  8. 'or_test', 'and_test', 'not_test', 'comparison', 'expr',
  9. 'xor_expr', 'and_expr', 'shift_expr', 'arith_expr',
  10. 'atom_expr', 'term', 'factor', 'power', 'atom'}
  11. _FLOW_KEYWORDS = (
  12. 'try', 'except', 'finally', 'else', 'if', 'elif', 'with', 'for', 'while'
  13. )
  14. def get_executable_nodes(node, last_added=False):
  15. """
  16. For static analysis.
  17. """
  18. result = []
  19. typ = node.type
  20. if typ == 'name':
  21. next_leaf = node.get_next_leaf()
  22. if last_added is False and node.parent.type != 'param' and next_leaf != '=':
  23. result.append(node)
  24. elif typ == 'expr_stmt':
  25. # I think evaluating the statement (and possibly returned arrays),
  26. # should be enough for static analysis.
  27. result.append(node)
  28. for child in node.children:
  29. result += get_executable_nodes(child, last_added=True)
  30. elif typ == 'decorator':
  31. # decorator
  32. if node.children[-2] == ')':
  33. node = node.children[-3]
  34. if node != '(':
  35. result += get_executable_nodes(node)
  36. else:
  37. try:
  38. children = node.children
  39. except AttributeError:
  40. pass
  41. else:
  42. if node.type in _EXECUTE_NODES and not last_added:
  43. result.append(node)
  44. for child in children:
  45. result += get_executable_nodes(child, last_added)
  46. return result
  47. def get_comp_fors(comp_for):
  48. yield comp_for
  49. last = comp_for.children[-1]
  50. while True:
  51. if last.type == 'comp_for':
  52. yield last
  53. elif not last.type == 'comp_if':
  54. break
  55. last = last.children[-1]
  56. def for_stmt_defines_one_name(for_stmt):
  57. """
  58. Returns True if only one name is returned: ``for x in y``.
  59. Returns False if the for loop is more complicated: ``for x, z in y``.
  60. :returns: bool
  61. """
  62. return for_stmt.children[1].type == 'name'
  63. def get_flow_branch_keyword(flow_node, node):
  64. start_pos = node.start_pos
  65. if not (flow_node.start_pos < start_pos <= flow_node.end_pos):
  66. raise ValueError('The node is not part of the flow.')
  67. keyword = None
  68. for i, child in enumerate(flow_node.children):
  69. if start_pos < child.start_pos:
  70. return keyword
  71. first_leaf = child.get_first_leaf()
  72. if first_leaf in _FLOW_KEYWORDS:
  73. keyword = first_leaf
  74. return 0
  75. def get_statement_of_position(node, pos):
  76. for c in node.children:
  77. if c.start_pos <= pos <= c.end_pos:
  78. if c.type not in ('decorated', 'simple_stmt', 'suite',
  79. 'async_stmt', 'async_funcdef') \
  80. and not isinstance(c, (tree.Flow, tree.ClassOrFunc)):
  81. return c
  82. else:
  83. try:
  84. return get_statement_of_position(c, pos)
  85. except AttributeError:
  86. pass # Must be a non-scope
  87. return None
  88. def clean_scope_docstring(scope_node):
  89. """ Returns a cleaned version of the docstring token. """
  90. node = scope_node.get_doc_node()
  91. if node is not None:
  92. # TODO We have to check next leaves until there are no new
  93. # leaves anymore that might be part of the docstring. A
  94. # docstring can also look like this: ``'foo' 'bar'
  95. # Returns a literal cleaned version of the ``Token``.
  96. cleaned = cleandoc(safe_literal_eval(node.value))
  97. # Since we want the docstr output to be always unicode, just
  98. # force it.
  99. return force_unicode(cleaned)
  100. return ''
  101. def safe_literal_eval(value):
  102. first_two = value[:2].lower()
  103. if first_two[0] == 'f' or first_two in ('fr', 'rf'):
  104. # literal_eval is not able to resovle f literals. We have to do that
  105. # manually, but that's right now not implemented.
  106. return ''
  107. try:
  108. return literal_eval(value)
  109. except SyntaxError:
  110. # It's possible to create syntax errors with literals like rb'' in
  111. # Python 2. This should not be possible and in that case just return an
  112. # empty string.
  113. # Before Python 3.3 there was a more strict definition in which order
  114. # you could define literals.
  115. return ''
  116. def get_call_signature(funcdef, width=72, call_string=None):
  117. """
  118. Generate call signature of this function.
  119. :param width: Fold lines if a line is longer than this value.
  120. :type width: int
  121. :arg func_name: Override function name when given.
  122. :type func_name: str
  123. :rtype: str
  124. """
  125. # Lambdas have no name.
  126. if call_string is None:
  127. if funcdef.type == 'lambdef':
  128. call_string = '<lambda>'
  129. else:
  130. call_string = funcdef.name.value
  131. if funcdef.type == 'lambdef':
  132. p = '(' + ''.join(param.get_code() for param in funcdef.get_params()).strip() + ')'
  133. else:
  134. p = funcdef.children[2].get_code()
  135. p = re.sub(r'\s+', ' ', p)
  136. if funcdef.annotation:
  137. rtype = " ->" + funcdef.annotation.get_code()
  138. else:
  139. rtype = ""
  140. code = call_string + p + rtype
  141. return '\n'.join(textwrap.wrap(code, width))
  142. def get_doc_with_call_signature(scope_node):
  143. """
  144. Return a document string including call signature.
  145. """
  146. call_signature = None
  147. if scope_node.type == 'classdef':
  148. for funcdef in scope_node.iter_funcdefs():
  149. if funcdef.name.value == '__init__':
  150. call_signature = \
  151. get_call_signature(funcdef, call_string=scope_node.name.value)
  152. elif scope_node.type in ('funcdef', 'lambdef'):
  153. call_signature = get_call_signature(scope_node)
  154. doc = clean_scope_docstring(scope_node)
  155. if call_signature is None:
  156. return doc
  157. if not doc:
  158. return call_signature
  159. return '%s\n\n%s' % (call_signature, doc)
  160. def move(node, line_offset):
  161. """
  162. Move the `Node` start_pos.
  163. """
  164. try:
  165. children = node.children
  166. except AttributeError:
  167. node.line += line_offset
  168. else:
  169. for c in children:
  170. move(c, line_offset)
  171. def get_following_comment_same_line(node):
  172. """
  173. returns (as string) any comment that appears on the same line,
  174. after the node, including the #
  175. """
  176. try:
  177. if node.type == 'for_stmt':
  178. whitespace = node.children[5].get_first_leaf().prefix
  179. elif node.type == 'with_stmt':
  180. whitespace = node.children[3].get_first_leaf().prefix
  181. elif node.type == 'funcdef':
  182. # actually on the next line
  183. whitespace = node.children[4].get_first_leaf().get_next_leaf().prefix
  184. else:
  185. whitespace = node.get_last_leaf().get_next_leaf().prefix
  186. except AttributeError:
  187. return None
  188. except ValueError:
  189. # TODO in some particular cases, the tree doesn't seem to be linked
  190. # correctly
  191. return None
  192. if "#" not in whitespace:
  193. return None
  194. comment = whitespace[whitespace.index("#"):]
  195. if "\r" in comment:
  196. comment = comment[:comment.index("\r")]
  197. if "\n" in comment:
  198. comment = comment[:comment.index("\n")]
  199. return comment
  200. def is_scope(node):
  201. return node.type in ('file_input', 'classdef', 'funcdef', 'lambdef', 'comp_for')
  202. def get_parent_scope(node, include_flows=False):
  203. """
  204. Returns the underlying scope.
  205. """
  206. scope = node.parent
  207. while scope is not None:
  208. if include_flows and isinstance(scope, tree.Flow):
  209. return scope
  210. if is_scope(scope):
  211. break
  212. scope = scope.parent
  213. return scope
  214. def get_cached_code_lines(grammar, path):
  215. """
  216. Basically access the cached code lines in parso. This is not the nicest way
  217. to do this, but we avoid splitting all the lines again.
  218. """
  219. return parser_cache[grammar._hashed][path].lines