Repository URL to install this package:
|
Version:
2022.2.8 ▾
|
"""Tests for abstract.py."""
from pytype import config
from pytype import context
from pytype import errors
from pytype import load_pytd
from pytype import special_builtins
from pytype import state as frame_state
from pytype.abstract import abstract
from pytype.abstract import abstract_utils
from pytype.abstract import function
from pytype.pytd import pytd
from pytype.pytd import pytd_utils
from pytype.tests import test_base
from pytype.typegraph import cfg
import unittest
class AbstractTestBase(test_base.UnitTest):
def setUp(self):
super().setUp()
options = config.Options.create(python_version=self.python_version,
color="never")
self._ctx = context.Context(errors.ErrorLog(), options,
load_pytd.Loader(options))
self._program = self._ctx.program
self._node = self._ctx.root_node.ConnectNew("test_node")
def new_var(self, *values):
"""Create a Variable bound to the given values."""
var = self._program.NewVariable()
for value in values:
var.AddBinding(value, source_set=(), where=self._node)
return var
def new_dict(self, **kwargs):
"""Create a Dict from keywords mapping names to Variable objects."""
d = abstract.Dict(self._ctx)
for name, var in kwargs.items():
d.set_str_item(self._node, name, var)
return d
class IsInstanceTest(AbstractTestBase):
def setUp(self):
super().setUp()
self._is_instance = special_builtins.IsInstance.make(self._ctx)
# Easier access to some primitive instances.
self._bool = self._ctx.convert.primitive_class_instances[bool]
self._int = self._ctx.convert.primitive_class_instances[int]
self._str = self._ctx.convert.primitive_class_instances[str]
# Values that represent primitive classes.
self._obj_class = self._ctx.convert.primitive_classes[object]
self._int_class = self._ctx.convert.primitive_classes[int]
self._str_class = self._ctx.convert.primitive_classes[str]
def assert_call(self, expected, left, right):
"""Check that call() returned the desired results.
Args:
expected: A dict from values to source sets, where a source set is
represented by the sorted binding names separated by spaces, for
example "left:0 right:1" would indicate binding #0 of variable
"left" and binding #1 of variable "right".
left: A Variable to use as the first arg to call().
right: A Variable to use as the second arg to call().
"""
name_map = {left: "left", right: "right"}
node, result = self._is_instance.call(
self._node, None, function.Args(
(left, right), self.new_dict(), None, None))
self.assertIn(node, self._node.outgoing)
result_map = {}
# Turning source sets into canonical string representations of the binding
# names makes it much easier to debug failures.
for b in result.bindings:
terms = set()
for o in b.origins:
self.assertEqual(node, o.where)
for sources in o.source_sets:
terms.add(" ".join(sorted(
"%s:%d" % (name_map[b.variable], b.variable.bindings.index(b))
for b in sources)))
result_map[b.data] = terms
self.assertEqual(expected, result_map)
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_multiple_bindings(self):
left = self.new_var(self._int, self._str)
right = self.new_var(self._int_class, self._str_class)
self.assert_call(
{
self._ctx.convert.true: {"left:0 right:0", "left:1 right:1"},
self._ctx.convert.false: {"left:0 right:1", "left:1 right:0"},
}, left, right)
def test_call_wrong_argcount(self):
self._ctx.vm.push_frame(frame_state.SimpleFrame())
node, result = self._is_instance.call(
self._node, None, function.Args((), self.new_dict(), None, None))
self.assertEqual(self._node, node)
self.assertIsInstance(abstract_utils.get_atomic_value(result),
abstract.Unsolvable)
self.assertRegex(str(self._ctx.errorlog), "missing-parameter")
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_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 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)))
class PyTDTest(AbstractTestBase):
"""Tests for abstract -> pytd type conversions."""
def test_metaclass(self):
cls = abstract.InterpreterClass("X", [], {}, None, self._ctx)
meta = abstract.InterpreterClass("M", [], {}, None, self._ctx)
meta.official_name = "M"
cls.cls = meta
pytd_cls = cls.to_pytd_def(self._ctx.root_node, "X")
self.assertEqual(pytd_cls.metaclass, pytd.NamedType("M"))
def test_inherited_metaclass(self):
base = abstract.InterpreterClass("X", [], {}, None, self._ctx)
base.official_name = "X"
meta = abstract.InterpreterClass("M", [], {}, None, self._ctx)
meta.official_name = "M"
base.cls = meta
child = abstract.InterpreterClass("Y",
[base.to_variable(self._ctx.root_node)],
{}, None, self._ctx)
self.assertIs(child.cls, base.cls)
pytd_cls = child.to_pytd_def(self._ctx.root_node, "Y")
self.assertIs(pytd_cls.metaclass, None)
def test_metaclass_union(self):
cls = abstract.InterpreterClass("X", [], {}, None, self._ctx)
meta1 = abstract.InterpreterClass("M1", [], {}, None, self._ctx)
meta2 = abstract.InterpreterClass("M2", [], {}, None, self._ctx)
meta1.official_name = "M1"
meta2.official_name = "M2"
cls.cls = abstract.Union([meta1, meta2], self._ctx)
pytd_cls = cls.to_pytd_def(self._ctx.root_node, "X")
self.assertEqual(pytd_cls.metaclass, pytd.UnionType(
(pytd.NamedType("M1"), pytd.NamedType("M2"))))
def test_to_type_with_view1(self):
# to_type(<instance of List[int or unsolvable]>, view={T: int})
instance = abstract.List([], self._ctx)
instance.merge_instance_type_parameter(
self._ctx.root_node, abstract_utils.T,
self._ctx.program.NewVariable([self._ctx.convert.unsolvable], [],
self._ctx.root_node))
param_binding = instance.get_instance_type_parameter(
abstract_utils.T).AddBinding(
self._ctx.convert.primitive_class_instances[int], [],
self._ctx.root_node)
view = {
instance.get_instance_type_parameter(abstract_utils.T): param_binding}
pytd_type = instance.to_type(self._ctx.root_node, seen=None, view=view)
self.assertEqual("builtins.list", pytd_type.name)
self.assertSetEqual({"builtins.int"},
{t.name for t in pytd_type.parameters})
def test_to_type_with_view2(self):
# to_type(<tuple (int or str,)>, view={0: str})
param1 = self._ctx.convert.primitive_class_instances[int]
param2 = self._ctx.convert.primitive_class_instances[str]
param_var = param1.to_variable(self._ctx.root_node)
str_binding = param_var.AddBinding(param2, [], self._ctx.root_node)
instance = self._ctx.convert.tuple_to_value((param_var,))
view = {param_var: str_binding}
pytd_type = instance.to_type(self._ctx.root_node, seen=None, view=view)
self.assertEqual(pytd_type.parameters[0],
pytd.NamedType("builtins.str"))
def test_to_type_with_view_and_empty_param(self):
instance = abstract.List([], self._ctx)
pytd_type = instance.to_type(self._ctx.root_node, seen=None, view={})
self.assertEqual("builtins.list", pytd_type.name)
self.assertSequenceEqual((pytd.NothingType(),), pytd_type.parameters)
def test_typing_container(self):
cls = self._ctx.convert.list_type
container = abstract.AnnotationContainer("List", self._ctx, cls)
expected = pytd.GenericType(pytd.NamedType("builtins.list"),
(pytd.AnythingType(),))
actual = container.get_instance_type(self._ctx.root_node)
self.assertEqual(expected, actual)
# TODO(rechen): Test InterpreterFunction.
class FunctionTest(AbstractTestBase):
def _make_pytd_function(self, params, name="f"):
pytd_params = []
for i, p in enumerate(params):
p_type = pytd.ClassType(p.name)
p_type.cls = p
pytd_params.append(
pytd.Parameter(function.argname(i), p_type, False, False, None))
pytd_sig = pytd.Signature(
tuple(pytd_params), None, None, pytd.AnythingType(), (), ())
sig = function.PyTDSignature(name, pytd_sig, self._ctx)
return abstract.PyTDFunction(name, (sig,), pytd.MethodTypes.METHOD,
self._ctx)
def _call_pytd_function(self, f, args):
b = f.to_binding(self._ctx.root_node)
return f.call(self._ctx.root_node, b, function.Args(posargs=args))
def test_call_with_empty_arg(self):
self.assertRaises(AssertionError, self._call_pytd_function,
self._make_pytd_function(params=()),
(self._ctx.program.NewVariable(),))
def test_call_with_bad_arg(self):
f = self._make_pytd_function(
(self._ctx.loader.lookup_builtin("builtins.str"),))
arg = self._ctx.convert.primitive_class_instances[int].to_variable(
self._ctx.root_node)
self.assertRaises(
function.WrongArgTypes, self._call_pytd_function, f, (arg,))
def test_simple_call(self):
f = self._make_pytd_function(
(self._ctx.loader.lookup_builtin("builtins.str"),))
arg = self._ctx.convert.primitive_class_instances[str].to_variable(
self._ctx.root_node)
node, ret = self._call_pytd_function(f, (arg,))
self.assertIs(node, self._ctx.root_node)
retval, = ret.bindings
self.assertIs(retval.data, self._ctx.convert.unsolvable)
def test_call_with_multiple_arg_bindings(self):
f = self._make_pytd_function(
(self._ctx.loader.lookup_builtin("builtins.str"),))
arg = self._ctx.program.NewVariable()
arg.AddBinding(self._ctx.convert.primitive_class_instances[str], [],
self._ctx.root_node)
arg.AddBinding(self._ctx.convert.primitive_class_instances[int], [],
self._ctx.root_node)
node, ret = self._call_pytd_function(f, (arg,))
self.assertIs(node, self._ctx.root_node)
retval, = ret.bindings
self.assertIs(retval.data, self._ctx.convert.unsolvable)
def test_call_with_skipped_combination(self):
f = self._make_pytd_function(
(self._ctx.loader.lookup_builtin("builtins.str"),))
node = self._ctx.root_node.ConnectNew()
arg = self._ctx.convert.primitive_class_instances[str].to_variable(node)
node, ret = self._call_pytd_function(f, (arg,))
self.assertIs(node, self._ctx.root_node)
self.assertFalse(ret.bindings)
def test_signature_from_pytd(self):
# def f(self: Any, *args: Any)
self_param = pytd.Parameter("self", pytd.AnythingType(), False, False, None)
args_param = pytd.Parameter("args", pytd.AnythingType(), False, True, None)
sig = function.Signature.from_pytd(
self._ctx, "f",
pytd.Signature((self_param,), args_param, None, pytd.AnythingType(), (),
()))
self.assertEqual(repr(sig), "def f(self: Any, *args: Any) -> Any")
self.assertEqual(sig.name, "f")
self.assertSequenceEqual(sig.param_names, ("self",))
self.assertEqual(sig.varargs_name, "args")
self.assertFalse(sig.kwonly_params)
self.assertIs(sig.kwargs_name, None)
self.assertSetEqual(set(sig.annotations), {"self", "args", "return"})
self.assertTrue(sig.has_return_annotation)
self.assertTrue(sig.has_param_annotations)
def test_signature_from_callable(self):
# Callable[[int, str], Any]
params = {0: self._ctx.convert.int_type, 1: self._ctx.convert.str_type}
params[abstract_utils.ARGS] = abstract.Union((params[0], params[1]),
self._ctx)
params[abstract_utils.RET] = self._ctx.convert.unsolvable
callable_val = abstract.CallableClass(self._ctx.convert.function_type,
params, self._ctx)
sig = function.Signature.from_callable(callable_val)
self.assertEqual(repr(sig), "def <callable>(_0: int, _1: str) -> Any")
self.assertEqual(sig.name, "<callable>")
self.assertSequenceEqual(sig.param_names, ("_0", "_1"))
self.assertIs(sig.varargs_name, None)
self.assertFalse(sig.kwonly_params)
self.assertIs(sig.kwargs_name, None)
self.assertCountEqual(sig.annotations.keys(), sig.param_names)
self.assertFalse(sig.has_return_annotation)
self.assertTrue(sig.has_param_annotations)
def test_signature_annotations(self):
# def f(self: Any, *args: Any)
self_param = pytd.Parameter("self", pytd.AnythingType(), False, False, None)
# Imitate the parser's conversion of '*args: Any' to '*args: Tuple[Any]'.
tup = pytd.ClassType("builtins.tuple")
tup.cls = self._ctx.convert.tuple_type.pytd_cls
any_tuple = pytd.GenericType(tup, (pytd.AnythingType(),))
args_param = pytd.Parameter("args", any_tuple, False, True, None)
sig = function.Signature.from_pytd(
self._ctx, "f",
pytd.Signature((self_param,), args_param, None, pytd.AnythingType(), (),
()))
self.assertEqual(repr(sig),
"def f(self: Any, *args: Tuple[Any, ...]) -> Any")
self.assertIs(sig.annotations["self"], self._ctx.convert.unsolvable)
args_type = sig.annotations["args"]
self.assertIsInstance(args_type, abstract.ParameterizedClass)
self.assertIs(args_type.base_cls, self._ctx.convert.tuple_type)
self.assertListEqual(
list(args_type.formal_type_parameters.items()),
[(abstract_utils.T, self._ctx.convert.unsolvable)])
self.assertIs(sig.drop_first_parameter().annotations["args"], args_type)
def test_signature_annotations_existence(self):
# def f(v: "X") -> "Y"
sig = function.Signature(
name="f",
param_names=("v",),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={},
)
self.assertFalse(sig.has_param_annotations)
self.assertFalse(sig.has_return_annotation)
sig.set_annotation("v", self._ctx.convert.unsolvable)
self.assertTrue(sig.has_param_annotations)
self.assertFalse(sig.has_return_annotation)
sig.set_annotation("return", self._ctx.convert.unsolvable)
self.assertTrue(sig.has_param_annotations)
self.assertTrue(sig.has_return_annotation)
def test_signature_posarg_only_param_count(self):
# def f(x): ...
sig = function.Signature(
name="f",
param_names=("x",),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={},
)
self.assertEqual(repr(sig), "def f(x) -> Any")
self.assertEqual(sig.mandatory_param_count(), 1)
self.assertEqual(sig.maximum_param_count(), 1)
def test_signature_posarg_and_kwarg_param_count(self):
# def f(x, y=None): ...
sig = function.Signature(
name="f",
param_names=(
"x",
"y",
),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={"y": self._ctx.convert.none_type.to_variable(self._node)},
annotations={},
)
self.assertEqual(repr(sig), "def f(x, y = None) -> Any")
self.assertEqual(sig.mandatory_param_count(), 1)
self.assertEqual(sig.maximum_param_count(), 2)
def test_signature_varargs_param_count(self):
# def f(*args): ...
sig = function.Signature(
name="f",
param_names=(),
varargs_name="args",
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={},
)
self.assertEqual(repr(sig), "def f(*args) -> Any")
self.assertEqual(sig.mandatory_param_count(), 0)
self.assertIsNone(sig.maximum_param_count())
def test_signature_kwargs_param_count(self):
# def f(**kwargs): ...
sig = function.Signature(
name="f",
param_names=(),
varargs_name=None,
kwonly_params=(),
kwargs_name="kwargs",
defaults={},
annotations={},
)
self.assertEqual(repr(sig), "def f(**kwargs) -> Any")
self.assertEqual(sig.mandatory_param_count(), 0)
self.assertIsNone(sig.maximum_param_count())
def test_signature_kwonly_param_count(self):
# def f(*, y=None): ...
sig = function.Signature(
name="f",
param_names=(),
varargs_name=None,
kwonly_params=("y",),
kwargs_name=None,
defaults={"y": self._ctx.convert.none_type.to_variable(self._node)},
annotations={},
)
self.assertEqual(repr(sig), "def f(*, y = None) -> Any")
self.assertEqual(sig.mandatory_param_count(), 0)
self.assertEqual(sig.maximum_param_count(), 1)
def test_signature_has_param(self):
# def f(x, *args, y, **kwargs): ...
sig = function.Signature(
name="f",
param_names=("x",),
varargs_name="args",
kwonly_params={"y"},
kwargs_name="kwargs",
defaults={},
annotations={},
)
self.assertEqual(repr(sig), "def f(x, *args, y, **kwargs) -> Any")
for param in ("x", "args", "y", "kwargs"):
self.assertTrue(sig.has_param(param))
self.assertFalse(sig.has_param("rumpelstiltskin"))
def test_signature_insert_varargs_and_kwargs(self):
# def f(x, *args, y, **kwargs): ...
sig = function.Signature(
name="f",
param_names=("x",),
varargs_name="args",
kwonly_params={"y"},
kwargs_name="kwargs",
defaults={},
annotations={},
)
# f(1, 2, y=3, z=4)
int_inst = self._ctx.convert.primitive_class_instances[int]
int_binding = int_inst.to_binding(self._node)
arg_dict = {
"x": int_binding, "_1": int_binding, "y": int_binding, "z": int_binding}
sig = sig.insert_varargs_and_kwargs(arg_dict)
self.assertEqual(sig.name, "f")
self.assertSequenceEqual(sig.param_names, ("x", "_1", "z"))
self.assertEqual(sig.varargs_name, "args")
self.assertSetEqual(sig.kwonly_params, {"y"})
self.assertEqual(sig.kwargs_name, "kwargs")
self.assertFalse(sig.annotations)
def test_signature_del_param_annotation(self):
# def f(x) -> int: ...
sig = function.Signature(
name="f",
param_names=("x",),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={
"x": self._ctx.convert.unsolvable,
"return": self._ctx.convert.unsolvable
},
)
sig.del_annotation("x")
self.assertCountEqual(sig.annotations.keys(), {"return"})
self.assertFalse(sig.has_param_annotations)
self.assertTrue(sig.has_return_annotation)
def test_signature_del_return_annotation(self):
# def f(x) -> int: ...
sig = function.Signature(
name="f",
param_names=("x",),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={
"x": self._ctx.convert.unsolvable,
"return": self._ctx.convert.unsolvable
},
)
sig.del_annotation("return")
self.assertCountEqual(sig.annotations.keys(), {"x"})
self.assertTrue(sig.has_param_annotations)
self.assertFalse(sig.has_return_annotation)
def test_signature_del_nonexistent_annotation(self):
# def f(): ...
sig = function.Signature(
name="f",
param_names=(),
varargs_name=None,
kwonly_params=(),
kwargs_name=None,
defaults={},
annotations={},
)
self.assertRaises(KeyError, sig.del_annotation, "rumpelstiltskin")
def test_constructor_args(self):
f = abstract.PyTDFunction.make("open", self._ctx, "builtins")
self.assertEqual(f.full_name, "builtins.open")
self.assertCountEqual(
{sig.pytd_sig for sig in f.signatures},
self._ctx.loader.lookup_builtin("builtins.open").signatures)
self.assertIs(f.kind, pytd.MethodTypes.METHOD)
self.assertIs(f.ctx.vm, self._ctx.vm)
def test_constructor_args_pyval(self):
sig = pytd.Signature((), None, None, pytd.AnythingType(), (), ())
pyval = pytd.Function("blah", (sig,), pytd.MethodTypes.STATICMETHOD, 0)
f = abstract.PyTDFunction.make("open", self._ctx, "builtins", pyval=pyval)
self.assertEqual(f.full_name, "builtins.open")
f_sig, = f.signatures
self.assertIs(f_sig.pytd_sig, sig)
self.assertIs(f.kind, pytd.MethodTypes.STATICMETHOD)
self.assertIs(f.ctx.vm, self._ctx.vm)
def test_get_constructor_args(self):
f = abstract.PyTDFunction.make(
"TypeVar", self._ctx, "typing", pyval_name="_typevar_new")
self.assertEqual(f.full_name, "typing.TypeVar")
self.assertCountEqual({sig.pytd_sig for sig in f.signatures},
self._ctx.loader.import_name("typing").Lookup(
"typing._typevar_new").signatures)
self.assertIs(f.kind, pytd.MethodTypes.METHOD)
self.assertIs(f.ctx.vm, self._ctx.vm)
def test_bound_function_repr(self):
f = self._make_pytd_function(params=())
callself = self._ctx.program.NewVariable(
[abstract.BaseValue(name, self._ctx) for name in ("test1", "test2")],
[], self._ctx.root_node)
bound = abstract.BoundFunction(callself, f)
self.assertCountEqual(bound.repr_names(), ["test1.f", "test2.f"])
self.assertRegex(repr(bound), r"test(1|2)\.f")
def test_bound_function_callself_repr(self):
f = self._make_pytd_function(params=())
callself = self._ctx.program.NewVariable(
[abstract.BaseValue("test", self._ctx)], [], self._ctx.root_node)
bound = abstract.BoundFunction(callself, f)
callself_repr = lambda v: v.name + "foo"
self.assertCountEqual(bound.repr_names(callself_repr), ["testfoo.f"])
def test_bound_function_nested_repr(self):
f = self._make_pytd_function(params=())
callself1 = self._ctx.program.NewVariable(
[abstract.BaseValue("test1", self._ctx)], [], self._ctx.root_node)
bound1 = abstract.BoundFunction(callself1, f)
callself2 = self._ctx.program.NewVariable(
[abstract.BaseValue("test2", self._ctx)], [], self._ctx.root_node)
bound2 = abstract.BoundFunction(callself2, bound1)
# `bound2` is BoundFunction(test2, BoundFunction(test1, f))
self.assertCountEqual(bound2.repr_names(), ["test2.f"])
def test_bound_function_repr_no_callself(self):
f = self._make_pytd_function(params=())
callself = self._ctx.program.NewVariable()
bound = abstract.BoundFunction(callself, f)
self.assertCountEqual(bound.repr_names(), ["<class>.f"])
def test_bound_function_repr_replace_parent(self):
f = self._make_pytd_function(params=(), name="foo.f")
callself = self._ctx.program.NewVariable(
[abstract.BaseValue("test", self._ctx)], [], self._ctx.root_node)
bound = abstract.BoundFunction(callself, f)
self.assertCountEqual(bound.repr_names(), ["test.f"])
class AbstractMethodsTest(AbstractTestBase):
def test_abstract_method(self):
func = abstract.Function("f", self._ctx).to_variable(self._ctx.root_node)
func.data[0].is_abstract = True
cls = abstract.InterpreterClass("X", [], {"f": func}, None, self._ctx)
self.assertCountEqual(cls.abstract_methods, {"f"})
def test_inherited_abstract_method(self):
sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized")
sized = self._ctx.convert.constant_to_value(sized_pytd, {},
self._ctx.root_node)
cls = abstract.InterpreterClass("X",
[sized.to_variable(self._ctx.root_node)],
{}, None, self._ctx)
self.assertCountEqual(cls.abstract_methods, {"__len__"})
def test_overridden_abstract_method(self):
sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized")
sized = self._ctx.convert.constant_to_value(sized_pytd, {},
self._ctx.root_node)
bases = [sized.to_variable(self._ctx.root_node)]
members = {"__len__": self._ctx.new_unsolvable(self._ctx.root_node)}
cls = abstract.InterpreterClass("X", bases, members, None, self._ctx)
self.assertFalse(cls.abstract_methods)
def test_overridden_abstract_method_still_abstract(self):
sized_pytd = self._ctx.loader.typing.Lookup("typing.Sized")
sized = self._ctx.convert.constant_to_value(sized_pytd, {},
self._ctx.root_node)
bases = [sized.to_variable(self._ctx.root_node)]
func = abstract.Function("__len__", self._ctx)
func.is_abstract = True
members = {"__len__": func.to_variable(self._ctx.root_node)}
cls = abstract.InterpreterClass("X", bases, members, None, self._ctx)
self.assertCountEqual(cls.abstract_methods, {"__len__"})
class SimpleFunctionTest(AbstractTestBase):
def _make_func(self, name="_", param_names=None, varargs_name=None,
kwonly_params=(), kwargs_name=None, defaults=(),
annotations=None):
return abstract.SimpleFunction(name, param_names or (), varargs_name,
kwonly_params, kwargs_name, defaults,
annotations or {}, self._ctx)
def _simple_sig(self, param_types, ret_type=None):
annots = {("_%d" % i): t for i, t in enumerate(param_types)}
params = tuple(annots.keys())
if ret_type:
annots["return"] = ret_type
return self._make_func(param_names=params, annotations=annots)
def test_simple_call(self):
f = self._simple_sig([self._ctx.convert.str_type],
ret_type=self._ctx.convert.int_type)
args = function.Args(
(self._ctx.convert.build_string(self._ctx.root_node, "hello"),))
node, ret = f.call(self._ctx.root_node, f, args)
self.assertIs(node, self._ctx.root_node)
ret_val, = ret.data
self.assertEqual(ret_val.cls, self._ctx.convert.int_type)
def test_call_with_bad_arg(self):
f = self._make_func(
param_names=("test",), annotations={"test": self._ctx.convert.str_type})
args = function.Args((self._ctx.convert.build_int(self._ctx.root_node),))
self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f,
args)
def test_call_with_no_args(self):
f = self._simple_sig(
[self._ctx.convert.str_type, self._ctx.convert.int_type])
args = function.Args(())
self.assertRaises(function.MissingParameter, f.call, self._ctx.root_node, f,
args)
def test_call_with_multiple_arg_bindings(self):
f = self._simple_sig([self._ctx.convert.str_type])
arg = self._ctx.program.NewVariable()
arg.AddBinding(self._ctx.convert.primitive_class_instances[str], [],
self._ctx.root_node)
arg.AddBinding(self._ctx.convert.primitive_class_instances[int], [],
self._ctx.root_node)
args = function.Args((arg,))
node, ret = f.call(self._ctx.root_node, f, args)
self.assertIs(node, self._ctx.root_node)
self.assertIs(ret.data[0], self._ctx.convert.none)
def test_call_with_varargs(self):
f = self._make_func(
varargs_name="arg",
annotations={
"arg": self._ctx.convert.str_type,
"return": self._ctx.convert.str_type
})
starargs = self._ctx.convert.build_tuple(
self._ctx.root_node,
(self._ctx.convert.build_string(self._ctx.root_node, ""),))
args = function.Args(posargs=(), starargs=starargs)
node, ret = f.call(self._ctx.root_node, f, args)
self.assertIs(node, self._ctx.root_node)
self.assertIs(ret.data[0].cls, self._ctx.convert.str_type)
def test_call_with_bad_varargs(self):
f = self._make_func(
varargs_name="arg", annotations={"arg": self._ctx.convert.str_type})
starargs = self._ctx.convert.build_tuple(
self._ctx.root_node,
(self._ctx.convert.build_string(self._ctx.root_node, ""),
self._ctx.convert.build_int(self._ctx.root_node)))
args = function.Args(posargs=(), starargs=starargs)
self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f,
args)
def test_call_with_multiple_varargs_bindings(self):
f = self._make_func(
varargs_name="arg", annotations={"arg": self._ctx.convert.str_type})
arg = self._ctx.program.NewVariable()
arg.AddBinding(self._ctx.convert.primitive_class_instances[str], [],
self._ctx.root_node)
arg.AddBinding(self._ctx.convert.primitive_class_instances[int], [],
self._ctx.root_node)
starargs = self._ctx.convert.build_tuple(self._ctx.root_node, (arg,))
args = function.Args(posargs=(), starargs=starargs)
f.call(self._ctx.root_node, f, args)
def test_call_with_kwargs(self):
f = self._make_func(
kwargs_name="kwarg", annotations={"kwarg": self._ctx.convert.str_type})
kwargs = abstract.Dict(self._ctx)
kwargs.update(
self._ctx.root_node, {
"_1": self._ctx.convert.build_string(self._ctx.root_node, "1"),
"_2": self._ctx.convert.build_string(self._ctx.root_node, "2")
})
kwargs = kwargs.to_variable(self._ctx.root_node)
args = function.Args(
posargs=(), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs)
f.call(self._ctx.root_node, f, args)
def test_call_with_bad_kwargs(self):
f = self._make_func(
kwargs_name="kwarg", annotations={"kwarg": self._ctx.convert.str_type})
kwargs = abstract.Dict(self._ctx)
kwargs.update(self._ctx.root_node,
{"_1": self._ctx.convert.build_int(self._ctx.root_node)})
kwargs = kwargs.to_variable(self._ctx.root_node)
args = function.Args(
posargs=(), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs)
self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f,
args)
def test_call_with_kwonly_args(self):
f = self._make_func(
param_names=("test",),
kwonly_params=("a", "b"),
annotations={
"test": self._ctx.convert.str_type,
"a": self._ctx.convert.str_type,
"b": self._ctx.convert.str_type
})
kwargs = abstract.Dict(self._ctx)
kwargs.update(
self._ctx.root_node, {
"a": self._ctx.convert.build_string(self._ctx.root_node, "2"),
"b": self._ctx.convert.build_string(self._ctx.root_node, "3")
})
kwargs = kwargs.to_variable(self._ctx.root_node)
args = function.Args(
posargs=(self._ctx.convert.build_string(self._ctx.root_node, "1"),),
namedargs=abstract.Dict(self._ctx),
starstarargs=kwargs)
f.call(self._ctx.root_node, f, args)
kwargs = abstract.Dict(self._ctx)
kwargs.update(
self._ctx.root_node,
{"b": self._ctx.convert.build_string(self._ctx.root_node, "3")})
kwargs = kwargs.to_variable(self._ctx.root_node)
args = function.Args(
posargs=(self._ctx.convert.build_string(self._ctx.root_node, "1"),
self._ctx.convert.build_int(self._ctx.root_node)),
namedargs=abstract.Dict(self._ctx),
starstarargs=kwargs)
self.assertRaises(function.MissingParameter, f.call, self._ctx.root_node, f,
args)
def test_call_with_all_args(self):
f = self._make_func(
param_names=("a", "b", "c"),
varargs_name="arg",
kwargs_name="kwarg",
defaults=(self._ctx.convert.build_int(self._ctx.root_node),),
annotations={
"a": self._ctx.convert.str_type,
"b": self._ctx.convert.int_type,
"c": self._ctx.convert.int_type,
"arg": self._ctx.convert.primitive_classes[float],
"kwarg": self._ctx.convert.primitive_classes[bool]
})
posargs = (self._ctx.convert.build_string(self._ctx.root_node, "1"),
self._ctx.convert.build_int(self._ctx.root_node))
float_inst = self._ctx.convert.primitive_class_instances[float]
stararg = self._ctx.convert.build_tuple(
self._ctx.root_node, (float_inst.to_variable(self._ctx.root_node),))
namedargs = abstract.Dict(self._ctx)
kwarg = abstract.Dict(self._ctx)
kwarg.update(
self._ctx.root_node, {
"x": self._ctx.convert.build_bool(self._ctx.root_node),
"y": self._ctx.convert.build_bool(self._ctx.root_node)
})
kwarg = kwarg.to_variable(self._ctx.root_node)
args = function.Args(posargs, namedargs, stararg, kwarg)
f.call(self._ctx.root_node, f, args)
def test_call_with_defaults(self):
f = self._make_func(
param_names=("a", "b", "c"),
defaults=(self._ctx.convert.build_int(self._ctx.root_node),),
annotations={
"a": self._ctx.convert.int_type,
"b": self._ctx.convert.int_type,
"c": self._ctx.convert.int_type
})
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
f.call(self._ctx.root_node, f, args)
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
f.call(self._ctx.root_node, f, args)
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),))
self.assertRaises(function.MissingParameter, f.call, self._ctx.root_node, f,
args)
def test_call_with_bad_default(self):
f = self._make_func(
param_names=("a", "b"),
defaults=(self._ctx.convert.build_string(self._ctx.root_node, ""),),
annotations={
"a": self._ctx.convert.int_type,
"b": self._ctx.convert.str_type
})
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f,
args)
def test_call_with_duplicate_keyword(self):
f = self._simple_sig([self._ctx.convert.int_type] * 2)
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)),
namedargs={"_1": self._ctx.convert.build_int(self._ctx.root_node)})
self.assertRaises(function.DuplicateKeyword, f.call, self._ctx.root_node, f,
args)
def test_call_with_wrong_arg_count(self):
f = self._simple_sig([self._ctx.convert.int_type])
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
self.assertRaises(function.WrongArgCount, f.call, self._ctx.root_node, f,
args)
def test_change_defaults(self):
f = self._make_func(
param_names=("a", "b", "c"),
defaults=(self._ctx.convert.build_int(self._ctx.root_node),))
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
f.call(self._ctx.root_node, f, args)
new_defaults = self._ctx.convert.build_tuple(
self._ctx.root_node,
(self._ctx.convert.build_int(self._ctx.root_node),
self._ctx.convert.build_int(self._ctx.root_node)))
f.set_function_defaults(self._ctx.root_node, new_defaults)
f.call(self._ctx.root_node, f, args)
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),))
f.call(self._ctx.root_node, f, args)
def test_call_with_type_parameter(self):
ret_cls = abstract.ParameterizedClass(
self._ctx.convert.list_type,
{abstract_utils.T: abstract.TypeParameter(abstract_utils.T, self._ctx)},
self._ctx)
f = self._make_func(
param_names=("test",),
annotations={
"test": abstract.TypeParameter(abstract_utils.T, self._ctx),
"return": ret_cls
})
args = function.Args(
posargs=(self._ctx.convert.build_int(self._ctx.root_node),))
_, ret = f.call(self._ctx.root_node, f, args)
# ret is an Instance(ParameterizedClass(list, {abstract_utils.T: int}))
# but we really only care about T.
self.assertIs(ret.data[0].cls.formal_type_parameters[abstract_utils.T],
self._ctx.convert.int_type)
def test_signature_func_output_basic(self):
node = self._ctx.root_node
f = self._make_func(name="basic", param_names=("a", "b"))
fp = self._ctx.pytd_convert.value_to_pytd_def(node, f, f.name)
self.assertEqual(pytd_utils.Print(fp), "def basic(a, b) -> None: ...")
def test_signature_func_output_annotations(self):
node = self._ctx.root_node
f = self._make_func(
name="annots",
param_names=("a", "b"),
annotations={
"a": self._ctx.convert.int_type,
"b": self._ctx.convert.str_type,
"return": self._ctx.convert.int_type
})
fp = self._ctx.pytd_convert.value_to_pytd_def(node, f, f.name)
self.assertEqual(pytd_utils.Print(fp),
"def annots(a: int, b: str) -> int: ...")
def test_signature_func_output(self):
node = self._ctx.root_node
dict_type = abstract.ParameterizedClass(
self._ctx.convert.dict_type, {
abstract_utils.K: self._ctx.convert.str_type,
abstract_utils.V: self._ctx.convert.int_type
}, self._ctx)
f = self._make_func(
name="test",
param_names=("a", "b"),
varargs_name="c",
kwonly_params=("d", "e"),
kwargs_name="f",
defaults={
"b": self._ctx.convert.build_int(node),
"d": self._ctx.convert.build_int(node)
},
annotations={
"a": self._ctx.convert.str_type,
"b": self._ctx.convert.int_type,
"c": self._ctx.convert.str_type,
"d": dict_type,
"e": self._ctx.convert.int_type,
"f": self._ctx.convert.str_type,
"return": self._ctx.convert.str_type
})
fp = self._ctx.pytd_convert.value_to_pytd_def(node, f, f.name)
f_str = ("def test(a: str, b: int = ..., *c: str, d: Dict[str, int] = ...,"
" e: int, **f: str) -> str: ...")
self.assertEqual(pytd_utils.Print(fp), f_str)
class AbstractTest(AbstractTestBase):
def test_interpreter_class_official_name(self):
cls = abstract.InterpreterClass("X", [], {}, None, self._ctx)
cls.update_official_name("Z")
self.assertEqual(cls.official_name, "Z")
cls.update_official_name("A") # takes effect because A < Z
self.assertEqual(cls.official_name, "A")
cls.update_official_name("Z") # no effect
self.assertEqual(cls.official_name, "A")
cls.update_official_name("X") # takes effect because X == cls.name
self.assertEqual(cls.official_name, "X")
cls.update_official_name("A") # no effect
self.assertEqual(cls.official_name, "X")
def test_type_parameter_official_name(self):
param = abstract.TypeParameter("T", self._ctx)
self._ctx.vm.frame = frame_state.SimpleFrame() # for error logging
param.update_official_name("T")
self.assertFalse(self._ctx.errorlog.has_error())
param.update_official_name("Q")
self.assertTrue(self._ctx.errorlog.has_error())
def test_type_parameter_equality(self):
param1 = abstract.TypeParameter("S", self._ctx)
param2 = abstract.TypeParameter("T", self._ctx)
cls = abstract.InterpreterClass("S", [], {}, None, self._ctx)
self.assertEqual(param1, param1)
self.assertNotEqual(param1, param2)
self.assertNotEqual(param1, cls)
def test_union_equality(self):
union1 = abstract.Union((self._ctx.convert.unsolvable,), self._ctx)
union2 = abstract.Union((self._ctx.convert.none,), self._ctx)
cls = abstract.InterpreterClass("Union", [], {}, None, self._ctx)
self.assertEqual(union1, union1)
self.assertNotEqual(union1, union2)
self.assertNotEqual(union1, cls)
def test_instantiate_type_parameter_type(self):
params = {
abstract_utils.T: abstract.TypeParameter(abstract_utils.T, self._ctx)
}
cls = abstract.ParameterizedClass(self._ctx.convert.type_type, params,
self._ctx)
self.assertListEqual(
cls.instantiate(self._node).data, [self._ctx.convert.unsolvable])
def test_super_type(self):
supercls = special_builtins.Super(self._ctx)
self.assertEqual(supercls.cls, self._ctx.convert.type_type)
def test_instantiate_interpreter_class(self):
cls = abstract.InterpreterClass("X", [], {}, None, self._ctx)
# When there is no current frame, create a new instance every time.
v1 = abstract_utils.get_atomic_value(cls.instantiate(self._node))
v2 = abstract_utils.get_atomic_value(cls.instantiate(self._node))
self.assertIsNot(v1, v2)
# Create one instance per opcode.
fake_opcode = object()
self._ctx.vm.push_frame(frame_state.SimpleFrame(fake_opcode))
v3 = abstract_utils.get_atomic_value(cls.instantiate(self._node))
v4 = abstract_utils.get_atomic_value(cls.instantiate(self._node))
self.assertIsNot(v1, v3)
self.assertIsNot(v2, v3)
self.assertIs(v3, v4)
def test_set_module_on_module(self):
# A module's 'module' attribute should always remain None, and no one
# should attempt to set it to something besides the module's name or None.
ast = pytd_utils.CreateModule("some_mod")
mod = abstract.Module(self._ctx, ast.name, {}, ast)
mod.module = ast.name
self.assertIsNone(mod.module)
self.assertEqual(ast.name, mod.full_name)
mod.module = None
self.assertIsNone(mod.module)
self.assertEqual(ast.name, mod.full_name)
def set_module():
mod.module = "other_mod"
self.assertRaises(AssertionError, set_module)
def test_call_type_parameter_instance(self):
instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
instance.merge_instance_type_parameter(
self._ctx.root_node, abstract_utils.T,
self._ctx.convert.int_type.to_variable(self._ctx.root_node))
t = abstract.TypeParameter(abstract_utils.T, self._ctx)
t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
function.Args(posargs=()))
self.assertIs(node, self._node)
retval, = ret.data
self.assertEqual(retval.cls, self._ctx.convert.int_type)
def test_call_empty_type_parameter_instance(self):
instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
t = abstract.TypeParameter(abstract_utils.T, self._ctx)
t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
function.Args(posargs=()))
self.assertIs(node, self._node)
retval, = ret.data
self.assertIs(retval, self._ctx.convert.empty)
def test_call_type_parameter_instance_with_wrong_args(self):
instance = abstract.Instance(self._ctx.convert.list_type, self._ctx)
instance.merge_instance_type_parameter(
self._ctx.root_node, abstract_utils.T,
self._ctx.convert.int_type.to_variable(self._ctx.root_node))
t = abstract.TypeParameter(abstract_utils.T, self._ctx)
t_instance = abstract.TypeParameterInstance(t, instance, self._ctx)
posargs = (self._ctx.new_unsolvable(self._node),) * 3
node, ret = t_instance.call(self._node, t_instance.to_binding(self._node),
function.Args(posargs=posargs))
self.assertIs(node, self._node)
self.assertTrue(ret.bindings)
error, = self._ctx.errorlog
self.assertEqual(error.name, "wrong-arg-count")
def test_instantiate_tuple_class_for_sub(self):
type_param = abstract.TypeParameter(abstract_utils.K, self._ctx)
cls = abstract.TupleClass(self._ctx.convert.tuple_type, {
0: type_param,
abstract_utils.T: type_param
}, self._ctx)
# Instantiate the tuple class.
subst_value = cls.instantiate(self._ctx.root_node,
abstract_utils.DUMMY_CONTAINER)
# Recover the class from the instance.
subbed_cls = self._ctx.annotation_utils.sub_one_annotation(
self._ctx.root_node, type_param, [{
abstract_utils.K: subst_value
}])
self.assertEqual(cls, subbed_cls)
def test_singleton(self):
self.assertIs(
abstract.Unsolvable(self._ctx), abstract.Unsolvable(self._ctx))
def test_singleton_subclass(self):
self.assertIs(abstract.Empty(self._ctx), abstract.Empty(self._ctx))
self.assertIsNot(abstract.Deleted(self._ctx), abstract.Empty(self._ctx))
if __name__ == "__main__":
unittest.main()