def _infer_end_subscript(node: nodes.Subscript, context=None): default = None if not isinstance(node.slice, nodes.Index) and not isinstance( node.slice.value, nodes.Const): raise inference.UseInferenceDefault t = get_prm_type(node.slice.value.value) if t is None: raise inference.UseInferenceDefault proxy = Z3Proxy(MANAGER.make_z3_var(node.slice.value.value, t), default) if t is bool and context.conditions_mode != context_mod.ConditionsMode.IN_PROGRESS: # yield all combinations of the bool, only in non-conditions expanding mode ast_true = nodes.Const(True) true_branch = inference.InferenceResult.load_result( ast_true, bound_conditions={proxy}, selected_operand={hash((node.slice.value.value, t)): ast_true}) yield true_branch ast_false = nodes.Const(False) false_branch = inference.InferenceResult.load_result( ast_false, bound_conditions={proxy.invert_condition()}, selected_operand={hash((node.slice.value.value, t)): ast_false}, ) yield false_branch else: yield inference.InferenceResult(proxy, status=True)
def test_link_1_var(self): # x = 2 parent = nodes.LocalsDictNode() ast_source_node = nodes.Assign(parent=parent) ast_source_node.postinit(targets=[ nodes.AssignName.quick_build(id="x", version=0, parent=ast_source_node) ], value=nodes.Const(2)) parent.locals = {"x_0": ast_source_node} # y = x ast_target_node = nodes.Assign(parent=parent) ast_target_node.postinit( targets=[ nodes.AssignName.quick_build(id="y", version=0, parent=ast_target_node) ], value=nodes.Name.quick_build(id="x", version=0, parent=ast_target_node), ) link_stmts_to_def(ast_target_node) assert ast_target_node.value.links == ast_source_node
def _infer(self, context=None, inferred_attr=None): if context and context.model is not None: try: z3_result = context.model.eval(self.value) defaults = [] for arg in z3.z3util.get_vars(z3_result): if arg in self.defaults: defaults.append( (arg, utilities.make_z3_const(self.defaults[arg]))) else: MANAGER.logger.info( "Z3", "Arg: {} from expr: {} is missing a default value", arg, z3_result) if defaults: z3_result = z3.substitute(z3_result, defaults) z3_result = context.model.eval(z3_result, model_completion=True) py_val = utilities.get_py_val_from_z3_val(z3_result) if type(py_val) is not bool: # don't add it in model used since bool value can't be change at later stage context.z3_model_used[self.value] = z3_result yield inference.InferenceResult.load_result( nodes.Const(py_val)) except NotImplementedError: yield inference.InferenceResult.load_result(self, substituted=True) else: yield inference.InferenceResult.load_result(self, substituted=True)
def _infer_ceil_division(node: nodes.Call, context): """ Ceiling division will be implemented by using (x + y - 1) / y taken from: https://stackoverflow.com/a/2745086/9677833 """ left = node.args[0].args[0].left.args[0] right = node.args[0].args[0].right x_p_y = nodes.BinOp() x_p_y.postinit(left, "+", right) x_p_y_min_1 = nodes.BinOp() x_p_y.parent = x_p_y_min_1 x_p_y_min_1.postinit(x_p_y, "-", nodes.Const(1)) x_p_y_min_1_div_y = nodes.BinOp() x_p_y_min_1.parent = x_p_y_min_1_div_y x_p_y_min_1_div_y.postinit(x_p_y_min_1, "/", right) MANAGER.apply_transform(x_p_y_min_1_div_y) yield from x_p_y_min_1_div_y.infer(context)
def infer_binop(node, context=None): # custom bin op inference here yield InferenceResult.load_result(nodes.Const(4))
def _infer_round(node, context=None): """ Interesting note in python 2 round function: round() will always return float. The way python 2 handles it is first called float(), which will triggers __float__, then perform rounding *without* delegating to __round__. __round__ is only available in python 3. The approach is here in dealing with type inference for python 2 is to just yield float type without getting the dunder method. """ if MANAGER.config.py_version == 2: # Python 2's round() will always return float yield inference.InferenceResult.load_type(float) return round_func = _py2_round if MANAGER.config.py_version == 2 else round builtin_func_repr = "round" builtin_dunder_func_repr = "__" + builtin_func_repr + "__" if 0 > len(node.args) > 2 or len(node.keywords) > 0: raise inference.UseInferenceDefault() num_arg = node.args[0] try: ndigit_arg = node.args[1] ndigit_arg_iter = node.args[1].infer(context) except IndexError: ndigit_arg = None ndigit_arg_iter = [ inference.InferenceResult.load_result(nodes.Const(0)) ] for number, ndigits in utilities.infer_product(num_arg.infer(context), ndigit_arg_iter): if number.status is False: # python 3's round() will delegate to __round__ # only the type is known. Yield the return signature based on the input argument # since __round__ is overloaded in builtins.pyi if number.result_type is not None: dunder_method = number.result_type.dunder_lookup( builtin_dunder_func_repr) if context.config.is_type_inference( ) and dunder_method is not None: if isinstance(dunder_method, nodes.OverloadedFunc): for res in dunder_method.get_return_type( *node.args, context=context): res += number + ndigits yield res else: yield inference.InferenceResult.load_type( dunder_method.get_return_type(), inference_results=(number, ndigits)) return try: if isinstance(number.result, nodes.Const): ndigits_value = None if ndigits.status is False else ndigits.result.value result = round_func(number.result.value, ndigits_value) for res in inference.const_factory(result): res += number + ndigits yield res continue elif isinstance(number.result, nodes.ClassInstance): # if it's a class instance, try to find __float__, map arg and infer the return value try: dunder_method = number.result.dunder_lookup( builtin_dunder_func_repr) except exceptions.VariableNotExistStackError: raise exceptions.DunderUnimplemented( builtin_dunder_func_repr, number.result.target_cls) else: # map the argument in abs() to the target dunder method context.map_args_to_func(number.result, ndigit_arg, func_node=dunder_method) for res in dunder_method.infer_return_value(context): res += number + ndigits yield res continue except ValueError: raise inference.UseInferenceDefault() pass