def _create_new_unknown_value(self, action): if not action or not self.ctx.vm.frame: return abstract.Unknown(self.ctx) # We allow only one Unknown at each point in the program, regardless of # what the call stack is. key = ("unknown", self.ctx.vm.frame.current_opcode, action) if key not in self._convert_cache: self._convert_cache[key] = abstract.Unknown(self.ctx) return self._convert_cache[key]
def test_call_single_bindings(self): right = self.new_var(self._str_class) left = self.new_var(self._str) self.assert_call({self._ctx.convert.true: {"left:0 right:0"}}, left, right) left = self.new_var(self._int) self.assert_call({self._ctx.convert.false: {"left:0 right:0"}}, left, right) left = self.new_var(abstract.Unknown(self._ctx)) self.assert_call( {self._bool: {"left:0 right:0"}}, left, right)
def test_call_wrong_keywords(self): self._ctx.vm.push_frame(frame_state.SimpleFrame()) x = self.new_var(abstract.Unknown(self._ctx)) node, result = self._is_instance.call( self._node, None, function.Args( (x, x), self.new_dict(foo=x), None, None)) self.assertEqual(self._node, node) self.assertIsInstance(abstract_utils.get_atomic_value(result), abstract.Unsolvable) self.assertRegex( str(self._ctx.errorlog), r"foo.*isinstance.*\[wrong-keyword-args\]")
def test_flatten(self): def maybe_var(v): return v if isinstance(v, cfg.Variable) else self.new_var(v) def new_tuple(*args): pyval = tuple(maybe_var(a) for a in args) return self._ctx.convert.tuple_to_value(pyval) def check(expected_ambiguous, expected_classes, value): classes = [] ambiguous = abstract_utils.flatten(value, classes) self.assertEqual(expected_ambiguous, ambiguous) self.assertEqual(expected_classes, classes) unknown = abstract.Unknown(self._ctx) # Simple values. check(False, [self._str_class], self._str_class) check(True, [], self._str) check(True, [], unknown) # (str, int) check(False, [self._str_class, self._int_class], new_tuple(self._str_class, self._int_class)) # (str, ?, int) check(True, [self._str_class, self._int_class], new_tuple(self._str_class, unknown, self._int_class)) # (str, (int, object)) check(False, [self._str_class, self._int_class, self._obj_class], new_tuple( self._str_class, new_tuple(self._int_class, self._obj_class))) # (str, (?, object)) check(True, [self._str_class, self._obj_class], new_tuple( self._str_class, new_tuple(unknown, self._obj_class))) # A variable with multiple bindings is ambiguous. # (str, int | object) check(True, [self._str_class], new_tuple(self._str_class, self.new_var(self._int_class, self._obj_class)))
def test_is_instance(self): def check(expected, left, right): self.assertEqual(expected, self._is_instance._is_instance(left, right)) # Unknown and Unsolvable are ambiguous. check(None, abstract.Unknown(self._ctx), self._obj_class) check(None, abstract.Unsolvable(self._ctx), self._obj_class) # If the object's class has multiple bindings, result is ambiguous. obj = abstract.SimpleValue("foo", self._ctx) check(None, obj, self._obj_class) obj.set_class(self._node, self.new_var( self._str_class, self._int_class)) check(None, obj, self._str_class) # If the class_spec is not a class, result is ambiguous. check(None, self._str, self._str) # Result is True/False depending on if the class is in the object's mro. check(True, self._str, self._obj_class) check(True, self._str, self._str_class) check(False, self._str, self._int_class)
def setUp(self): super().setUp() self._var = self._program.NewVariable() self._var.AddBinding(abstract.Unknown(self._ctx), [], self._node)
def test_compatible_with__after_unknown_update(self): # Updating an empty dict with an unknown value makes the former ambiguous. self._d.update(self._node, abstract.Unknown(self._ctx)) self.assertAmbiguous(self._d)