1 import re 2 import textwrap 3 from ast import literal_eval 4 from inspect import cleandoc 5 from weakref import WeakKeyDictionary 6 7 from parso.python import tree 8 from parso.cache import parser_cache 9 from parso import split_lines 10 11 _EXECUTE_NODES = {'funcdef', 'classdef', 'import_from', 'import_name', 'test', 12 'or_test', 'and_test', 'not_test', 'comparison', 'expr', 13 'xor_expr', 'and_expr', 'shift_expr', 'arith_expr', 14 'atom_expr', 'term', 'factor', 'power', 'atom'} 15 16 _FLOW_KEYWORDS = ( 17 'try', 'except', 'finally', 'else', 'if', 'elif', 'with', 'for', 'while' 18 ) 19 20 21 def get_executable_nodes(node, last_added=False): 22 """ 23 For static analysis. 24 """ 25 result = [] 26 typ = node.type 27 if typ == 'name': 28 next_leaf = node.get_next_leaf() 29 if last_added is False and node.parent.type != 'param' and next_leaf != '=': 30 result.append(node) 31 elif typ == 'expr_stmt': 32 # I think inferring the statement (and possibly returned arrays), 33 # should be enough for static analysis. 34 result.append(node) 35 for child in node.children: 36 result += get_executable_nodes(child, last_added=True) 37 elif typ == 'decorator': 38 # decorator 39 if node.children[-2] == ')': 40 node = node.children[-3] 41 if node != '(': 42 result += get_executable_nodes(node) 43 else: 44 try: 45 children = node.children 46 except AttributeError: 47 pass 48 else: 49 if node.type in _EXECUTE_NODES and not last_added: 50 result.append(node) 51 52 for child in children: 53 result += get_executable_nodes(child, last_added) 54 55 return result 56 57 58 def get_sync_comp_fors(comp_for): 59 yield comp_for 60 last = comp_for.children[-1] 61 while True: 62 if last.type == 'comp_for': 63 yield last.children[1] # Ignore the async. 64 elif last.type == 'sync_comp_for': 65 yield last 66 elif not last.type == 'comp_if': 67 break 68 last = last.children[-1] 69 70 71 def for_stmt_defines_one_name(for_stmt): 72 """ 73 Returns True if only one name is returned: ``for x in y``. 74 Returns False if the for loop is more complicated: ``for x, z in y``. 75 76 :returns: bool 77 """ 78 return for_stmt.children[1].type == 'name' 79 80 81 def get_flow_branch_keyword(flow_node, node): 82 start_pos = node.start_pos 83 if not (flow_node.start_pos < start_pos <= flow_node.end_pos): 84 raise ValueError('The node is not part of the flow.') 85 86 keyword = None 87 for i, child in enumerate(flow_node.children): 88 if start_pos < child.start_pos: 89 return keyword 90 first_leaf = child.get_first_leaf() 91 if first_leaf in _FLOW_KEYWORDS: 92 keyword = first_leaf 93 return None 94 95 96 def clean_scope_docstring(scope_node): 97 """ Returns a cleaned version of the docstring token. """ 98 node = scope_node.get_doc_node() 99 if node is not None: 100 # TODO We have to check next leaves until there are no new 101 # leaves anymore that might be part of the docstring. A 102 # docstring can also look like this: ``'foo' 'bar' 103 # Returns a literal cleaned version of the ``Token``. 104 return cleandoc(safe_literal_eval(node.value)) 105 return '' 106 107 108 def find_statement_documentation(tree_node): 109 if tree_node.type == 'expr_stmt': 110 tree_node = tree_node.parent # simple_stmt 111 maybe_string = tree_node.get_next_sibling() 112 if maybe_string is not None: 113 if maybe_string.type == 'simple_stmt': 114 maybe_string = maybe_string.children[0] 115 if maybe_string.type == 'string': 116 return cleandoc(safe_literal_eval(maybe_string.value)) 117 return '' 118 119 120 def safe_literal_eval(value): 121 first_two = value[:2].lower() 122 if first_two[0] == 'f' or first_two in ('fr', 'rf'): 123 # literal_eval is not able to resovle f literals. We have to do that 124 # manually, but that's right now not implemented. 125 return '' 126 127 return literal_eval(value) 128 129 130 def get_signature(funcdef, width=72, call_string=None, 131 omit_first_param=False, omit_return_annotation=False): 132 """ 133 Generate a string signature of a function. 134 135 :param width: Fold lines if a line is longer than this value. 136 :type width: int 137 :arg func_name: Override function name when given. 138 :type func_name: str 139 140 :rtype: str 141 """ 142 # Lambdas have no name. 143 if call_string is None: 144 if funcdef.type == 'lambdef': 145 call_string = '<lambda>' 146 else: 147 call_string = funcdef.name.value 148 params = funcdef.get_params() 149 if omit_first_param: 150 params = params[1:] 151 p = '(' + ''.join(param.get_code() for param in params).strip() + ')' 152 # TODO this is pretty bad, we should probably just normalize. 153 p = re.sub(r'\s+', ' ', p) 154 if funcdef.annotation and not omit_return_annotation: 155 rtype = " ->" + funcdef.annotation.get_code() 156 else: 157 rtype = "" 158 code = call_string + p + rtype 159 160 return '\n'.join(textwrap.wrap(code, width)) 161 162 163 def move(node, line_offset): 164 """ 165 Move the `Node` start_pos. 166 """ 167 try: 168 children = node.children 169 except AttributeError: 170 node.line += line_offset 171 else: 172 for c in children: 173 move(c, line_offset) 174 175 176 def get_following_comment_same_line(node): 177 """ 178 returns (as string) any comment that appears on the same line, 179 after the node, including the # 180 """ 181 try: 182 if node.type == 'for_stmt': 183 whitespace = node.children[5].get_first_leaf().prefix 184 elif node.type == 'with_stmt': 185 whitespace = node.children[3].get_first_leaf().prefix 186 elif node.type == 'funcdef': 187 # actually on the next line 188 whitespace = node.children[4].get_first_leaf().get_next_leaf().prefix 189 else: 190 whitespace = node.get_last_leaf().get_next_leaf().prefix 191 except AttributeError: 192 return None 193 except ValueError: 194 # TODO in some particular cases, the tree doesn't seem to be linked 195 # correctly 196 return None 197 if "#" not in whitespace: 198 return None 199 comment = whitespace[whitespace.index("#"):] 200 if "\r" in comment: 201 comment = comment[:comment.index("\r")] 202 if "\n" in comment: 203 comment = comment[:comment.index("\n")] 204 return comment 205 206 207 def is_scope(node): 208 t = node.type 209 if t == 'comp_for': 210 # Starting with Python 3.8, async is outside of the statement. 211 return node.children[1].type != 'sync_comp_for' 212 213 return t in ('file_input', 'classdef', 'funcdef', 'lambdef', 'sync_comp_for') 214 215 216 def _get_parent_scope_cache(func): 217 cache = WeakKeyDictionary() 218 219 def wrapper(parso_cache_node, node, include_flows=False): 220 if parso_cache_node is None: 221 return func(node, include_flows) 222 223 try: 224 for_module = cache[parso_cache_node] 225 except KeyError: 226 for_module = cache[parso_cache_node] = {} 227 228 try: 229 return for_module[node] 230 except KeyError: 231 result = for_module[node] = func(node, include_flows) 232 return result 233 return wrapper 234 235 236 def get_parent_scope(node, include_flows=False): 237 """ 238 Returns the underlying scope. 239 """ 240 scope = node.parent 241 if scope is None: 242 return None # It's a module already. 243 244 while True: 245 if is_scope(scope): 246 if scope.type in ('classdef', 'funcdef', 'lambdef'): 247 index = scope.children.index(':') 248 if scope.children[index].start_pos >= node.start_pos: 249 if node.parent.type == 'param' and node.parent.name == node: 250 pass 251 elif node.parent.type == 'tfpdef' and node.parent.children[0] == node: 252 pass 253 else: 254 scope = scope.parent 255 continue 256 return scope 257 elif include_flows and isinstance(scope, tree.Flow): 258 # The cursor might be on `if foo`, so the parent scope will not be 259 # the if, but the parent of the if. 260 if not (scope.type == 'if_stmt' 261 and any(n.start_pos <= node.start_pos < n.end_pos 262 for n in scope.get_test_nodes())): 263 return scope 264 265 scope = scope.parent 266 267 268 get_cached_parent_scope = _get_parent_scope_cache(get_parent_scope) 269 270 271 def get_cached_code_lines(grammar, path): 272 """ 273 Basically access the cached code lines in parso. This is not the nicest way 274 to do this, but we avoid splitting all the lines again. 275 """ 276 return get_parso_cache_node(grammar, path).lines 277 278 279 def get_parso_cache_node(grammar, path): 280 """ 281 This is of course not public. But as long as I control parso, this 282 shouldn't be a problem. ~ Dave 283 284 The reason for this is mostly caching. This is obviously also a sign of a 285 broken caching architecture. 286 """ 287 return parser_cache[grammar._hashed][path] 288 289 290 def cut_value_at_position(leaf, position): 291 """ 292 Cuts of the value of the leaf at position 293 """ 294 lines = split_lines(leaf.value, keepends=True)[:position[0] - leaf.line + 1] 295 column = position[1] 296 if leaf.line == position[0]: 297 column -= leaf.column 298 if not lines: 299 return '' 300 lines[-1] = lines[-1][:column] 301 return ''.join(lines) 302 303 304 def expr_is_dotted(node): 305 """ 306 Checks if a path looks like `name` or `name.foo.bar` and not `name()`. 307 """ 308 if node.type == 'atom': 309 if len(node.children) == 3 and node.children[0] == '(': 310 return expr_is_dotted(node.children[1]) 311 return False 312 if node.type == 'atom_expr': 313 children = node.children 314 if children[0] == 'await': 315 return False 316 if not expr_is_dotted(children[0]): 317 return False 318 # Check trailers 319 return all(c.children[0] == '.' for c in children[1:]) 320 return node.type == 'name' 321 322 323 def _function_is_x_method(decorator_checker): 324 def wrapper(function_node): 325 """ 326 This is a heuristic. It will not hold ALL the times, but it will be 327 correct pretty much for anyone that doesn't try to beat it. 328 staticmethod/classmethod are builtins and unless overwritten, this will 329 be correct. 330 """ 331 for decorator in function_node.get_decorators(): 332 dotted_name = decorator.children[1] 333 if decorator_checker(dotted_name.get_code()): 334 return True 335 return False 336 return wrapper 337 338 339 function_is_staticmethod = _function_is_x_method(lambda m: m == "staticmethod") 340 function_is_classmethod = _function_is_x_method(lambda m: m == "classmethod") 341 function_is_property = _function_is_x_method( 342 lambda m: m == "property" 343 or m == "cached_property" 344 or (m.endswith(".setter")) 345 ) 346