def _apply_decorators(context, node): """ Returns the function, that should to be executed in the end. This is also the places where the decorators are processed. """ if node.type == 'classdef': decoratee_value = ClassValue( context.inference_state, parent_context=context, tree_node=node ) else: decoratee_value = FunctionValue.from_context(context, node) initial = values = ValueSet([decoratee_value]) if is_big_annoying_library(context): return values for dec in reversed(node.get_decorators()): debug.dbg('decorator: %s %s', dec, values, color="MAGENTA") with debug.increase_indent_cm(): dec_values = context.infer_node(dec.children[1]) trailer_nodes = dec.children[2:-1] if trailer_nodes: # Create a trailer and infer it. trailer = tree.PythonNode('trailer', trailer_nodes) trailer.parent = dec dec_values = infer_trailer(context, dec_values, trailer) if not len(dec_values): code = dec.get_code(include_prefix=False) # For the short future, we don't want to hear about the runtime # decorator in typing that was intentionally omitted. This is not # "correct", but helps with debugging. if code != '@runtime\n': debug.warning('decorator not found: %s on %s', dec, node) return initial values = dec_values.execute(arguments.ValuesArguments([values])) if not len(values): debug.warning('not possible to resolve wrappers found %s', node) return initial debug.dbg('decorator end %s', values, color="MAGENTA") if values != initial: return ValueSet([Decoratee(c, decoratee_value) for c in values]) return values
def reachability_check(context, value_scope, node, origin_scope=None): if is_big_annoying_library(context) \ or not context.inference_state.flow_analysis_enabled: return UNSURE first_flow_scope = get_parent_scope(node, include_flows=True) if origin_scope is not None: origin_flow_scopes = list(_get_flow_scopes(origin_scope)) node_flow_scopes = list(_get_flow_scopes(node)) branch_matches = True for flow_scope in origin_flow_scopes: if flow_scope in node_flow_scopes: node_keyword = get_flow_branch_keyword(flow_scope, node) origin_keyword = get_flow_branch_keyword( flow_scope, origin_scope) branch_matches = node_keyword == origin_keyword if flow_scope.type == 'if_stmt': if not branch_matches: return UNREACHABLE elif flow_scope.type == 'try_stmt': if not branch_matches and origin_keyword == 'else' \ and node_keyword == 'except': return UNREACHABLE if branch_matches: break # Direct parents get resolved, we filter scopes that are separate # branches. This makes sense for autocompletion and static analysis. # For actual Python it doesn't matter, because we're talking about # potentially unreachable code. # e.g. `if 0:` would cause all name lookup within the flow make # unaccessible. This is not a "problem" in Python, because the code is # never called. In Jedi though, we still want to infer types. while origin_scope is not None: if first_flow_scope == origin_scope and branch_matches: return REACHABLE origin_scope = origin_scope.parent return _break_check(context, value_scope, first_flow_scope, node)
def py__getattribute__alternatives(self, string_name): ''' Since nothing was inferred, now check the __getattr__ and __getattribute__ methods. Stubs don't need to be checked, because they don't contain any logic. ''' if self.is_stub(): return NO_VALUES name = compiled.create_simple_object(self.inference_state, string_name) # This is a little bit special. `__getattribute__` is in Python # executed before `__getattr__`. But: I know no use case, where # this could be practical and where Jedi would return wrong types. # If you ever find something, let me know! # We are inversing this, because a hand-crafted `__getattribute__` # could still call another hand-crafted `__getattr__`, but not the # other way around. if is_big_annoying_library(self.parent_context): return NO_VALUES names = (self.get_function_slot_names(u'__getattr__') or self.get_function_slot_names(u'__getattribute__')) return self.execute_function_slots(names, name)
def process_params(param_names, star_count=3): # default means both * and ** if param_names: if is_big_annoying_library(param_names[0].parent_context): # At first this feature can look innocent, but it does a lot of # type inference in some cases, so we just ditch it. yield from param_names return used_names = set() arg_callables = [] kwarg_callables = [] kw_only_names = [] kwarg_names = [] arg_names = [] original_arg_name = None original_kwarg_name = None for p in param_names: kind = p.get_kind() if kind == Parameter.VAR_POSITIONAL: if star_count & 1: arg_callables = _iter_nodes_for_param(p) original_arg_name = p elif p.get_kind() == Parameter.VAR_KEYWORD: if star_count & 2: kwarg_callables = list(_iter_nodes_for_param(p)) original_kwarg_name = p elif kind == Parameter.KEYWORD_ONLY: if star_count & 2: kw_only_names.append(p) elif kind == Parameter.POSITIONAL_ONLY: if star_count & 1: yield p else: if star_count == 1: yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) elif star_count == 2: kw_only_names.append( ParamNameFixedKind(p, Parameter.KEYWORD_ONLY)) else: used_names.add(p.string_name) yield p # First process *args longest_param_names = () found_arg_signature = False found_kwarg_signature = False for func_and_argument in arg_callables: func, arguments = func_and_argument new_star_count = star_count if func_and_argument in kwarg_callables: kwarg_callables.remove(func_and_argument) else: new_star_count = 1 for signature in func.get_signatures(): found_arg_signature = True if new_star_count == 3: found_kwarg_signature = True args_for_this_func = [] for p in process_params( list( _remove_given_params( arguments, signature.get_param_names(resolve_stars=False))), new_star_count): if p.get_kind() == Parameter.VAR_KEYWORD: kwarg_names.append(p) elif p.get_kind() == Parameter.VAR_POSITIONAL: arg_names.append(p) elif p.get_kind() == Parameter.KEYWORD_ONLY: kw_only_names.append(p) else: args_for_this_func.append(p) if len(args_for_this_func) > len(longest_param_names): longest_param_names = args_for_this_func for p in longest_param_names: if star_count == 1 and p.get_kind() != Parameter.VAR_POSITIONAL: yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) else: if p.get_kind() == Parameter.POSITIONAL_OR_KEYWORD: used_names.add(p.string_name) yield p if not found_arg_signature and original_arg_name is not None: yield original_arg_name elif arg_names: yield arg_names[0] # Then process **kwargs for func, arguments in kwarg_callables: for signature in func.get_signatures(): found_kwarg_signature = True for p in process_params(list( _remove_given_params( arguments, signature.get_param_names(resolve_stars=False))), star_count=2): if p.get_kind() == Parameter.VAR_KEYWORD: kwarg_names.append(p) elif p.get_kind() == Parameter.KEYWORD_ONLY: kw_only_names.append(p) for p in kw_only_names: if p.string_name in used_names: continue yield p used_names.add(p.string_name) if not found_kwarg_signature and original_kwarg_name is not None: yield original_kwarg_name elif kwarg_names: yield kwarg_names[0]