""" Searching for names with given scope and name. This is very central in Jedi and Python. The name resolution is quite complicated with descripter, ``__getattribute__``, ``__getattr__``, ``global``, etc. If you want to understand name resolution, please read the first few chapters in http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/. Flow checks +++++++++++ Flow checks are not really mature. There's only a check for ``isinstance``. It would check whether a flow has the form of ``if isinstance(a, type_or_tuple)``. Unfortunately every other thing is being ignored (e.g. a == '' would be easy to check for -> a is a string). There's big potential in these checks. """ from parso.tree import search_ancestor from parso.python.tree import Name from jedi import settings from jedi.inference.arguments import TreeArguments from jedi.inference.value import iterable from jedi.inference.base_value import NO_VALUES from jedi.parser_utils import is_scope def filter_name(filters, name_or_str): """ Searches names that are defined in a scope (the different ``filters``), until a name fits. """ string_name = name_or_str.value if isinstance(name_or_str, Name) else name_or_str names = [] for filter in filters: names = filter.get(string_name) if names: break return list(_remove_del_stmt(names)) def _remove_del_stmt(names): # Catch del statements and remove them from results. for name in names: if name.tree_name is not None: definition = name.tree_name.get_definition() if definition is not None and definition.type == 'del_stmt': continue yield name def check_flow_information(value, flow, search_name, pos): """ Try to find out the type of a variable just with the information that is given by the flows: e.g. It is also responsible for assert checks.:: if isinstance(k, str): k. # <- completion here ensures that `k` is a string. """ if not settings.dynamic_flow_information: return None result = None if is_scope(flow): # Check for asserts. module_node = flow.get_root_node() try: names = module_node.get_used_names()[search_name.value] except KeyError: return None names = reversed([ n for n in names if flow.start_pos <= n.start_pos < (pos or flow.end_pos) ]) for name in names: ass = search_ancestor(name, 'assert_stmt') if ass is not None: result = _check_isinstance_type(value, ass.assertion, search_name) if result is not None: return result if flow.type in ('if_stmt', 'while_stmt'): potential_ifs = [c for c in flow.children[1::4] if c != ':'] for if_test in reversed(potential_ifs): if search_name.start_pos > if_test.end_pos: return _check_isinstance_type(value, if_test, search_name) return result def _get_isinstance_trailer_arglist(node): if node.type in ('power', 'atom_expr') and len(node.children) == 2: # This might be removed if we analyze and, etc first, trailer = node.children if first.type == 'name' and first.value == 'isinstance' \ and trailer.type == 'trailer' and trailer.children[0] == '(': return trailer return None def _check_isinstance_type(value, node, search_name): lazy_cls = None trailer = _get_isinstance_trailer_arglist(node) if trailer is not None and len(trailer.children) == 3: arglist = trailer.children[1] args = TreeArguments(value.inference_state, value, arglist, trailer) param_list = list(args.unpack()) # Disallow keyword arguments if len(param_list) == 2 and len(arglist.children) == 3: (key1, _), (key2, lazy_value_cls) = param_list if key1 is None and key2 is None: call = _get_call_string(search_name) is_instance_call = _get_call_string(arglist.children[0]) # Do a simple get_code comparison of the strings . They should # just have the same code, and everything will be all right. # There are ways that this is not correct, if some stuff is # redefined in between. However here we don't care, because # it's a heuristic that works pretty well. if call == is_instance_call: lazy_cls = lazy_value_cls if lazy_cls is None: return None value_set = NO_VALUES for cls_or_tup in lazy_cls.infer(): if isinstance(cls_or_tup, iterable.Sequence) and cls_or_tup.array_type == 'tuple': for lazy_value in cls_or_tup.py__iter__(): value_set |= lazy_value.infer().execute_with_values() else: value_set |= cls_or_tup.execute_with_values() return value_set def _get_call_string(node): if node.parent.type == 'atom_expr': return _get_call_string(node.parent) code = '' leaf = node.get_first_leaf() end = node.get_last_leaf().end_pos while leaf.start_pos < end: code += leaf.value leaf = leaf.get_next_leaf() return code