Ejemplo n.º 1
0
class FastCallTests(unittest.TestCase):
    """Test calling using various callables from C
    """

    # Test calls with positional arguments
    CALLS_POSARGS = [
        # (func, args: tuple, result)

        # Python function with 2 arguments
        (pyfunc, (1, 2), [1, 2]),

        # Python function without argument
        (pyfunc_noarg, (), "noarg"),

        # Python class methods
        (PythonClass.class_method, (), "classmethod"),
        (PythonClass.static_method, (), "staticmethod"),

        # Python instance methods
        (PYTHON_INSTANCE.method, (1, 2), [1, 2]),
        (PYTHON_INSTANCE.method_noarg, (), "noarg"),
        (PYTHON_INSTANCE.class_method, (), "classmethod"),
        (PYTHON_INSTANCE.static_method, (), "staticmethod"),

        # C callables are added later
    ]

    # Test calls with positional and keyword arguments
    CALLS_KWARGS = [
        # (func, args: tuple, kwargs: dict, result)

        # Python function with 2 arguments
        (pyfunc, (1, ), {
            'arg2': 2
        }, [1, 2]),
        (pyfunc, (), {
            'arg1': 1,
            'arg2': 2
        }, [1, 2]),

        # Python instance methods
        (PYTHON_INSTANCE.method, (1, ), {
            'arg2': 2
        }, [1, 2]),
        (PYTHON_INSTANCE.method, (), {
            'arg1': 1,
            'arg2': 2
        }, [1, 2]),

        # C callables are added later
    ]

    # Add all the calling conventions and variants of C callables
    _instance = _testcapi.MethInstance()
    for obj, expected_self in (
        (_testcapi, _testcapi),  # module-level function
        (_instance, _instance),  # bound method
        (_testcapi.MethClass, _testcapi.MethClass),  # class method on class
        (_testcapi.MethClass(), _testcapi.MethClass),  # class method on inst.
        (_testcapi.MethStatic, None),  # static method
    ):
        CALLS_POSARGS.extend([
            (obj.meth_varargs, (1, 2), (expected_self, (1, 2))),
            (obj.meth_varargs_keywords, (1, 2), (expected_self, (1, 2),
                                                 NULL_OR_EMPTY)),
            (obj.meth_fastcall, (1, 2), (expected_self, (1, 2))),
            (obj.meth_fastcall, (), (expected_self, ())),
            (obj.meth_fastcall_keywords, (1, 2), (expected_self, (1, 2),
                                                  NULL_OR_EMPTY)),
            (obj.meth_fastcall_keywords, (), (expected_self, (),
                                              NULL_OR_EMPTY)),
            (obj.meth_noargs, (), expected_self),
            (obj.meth_o, (123, ), (expected_self, 123)),
        ])

        CALLS_KWARGS.extend([
            (obj.meth_varargs_keywords, (1, 2), {
                'x': 'y'
            }, (expected_self, (1, 2), {
                'x': 'y'
            })),
            (obj.meth_varargs_keywords, (), {
                'x': 'y'
            }, (expected_self, (), {
                'x': 'y'
            })),
            (obj.meth_varargs_keywords, (1, 2), {}, (expected_self, (1, 2),
                                                     NULL_OR_EMPTY)),
            (obj.meth_fastcall_keywords, (1, 2), {
                'x': 'y'
            }, (expected_self, (1, 2), {
                'x': 'y'
            })),
            (obj.meth_fastcall_keywords, (), {
                'x': 'y'
            }, (expected_self, (), {
                'x': 'y'
            })),
            (obj.meth_fastcall_keywords, (1, 2), {}, (expected_self, (1, 2),
                                                      NULL_OR_EMPTY)),
        ])

    def check_result(self, result, expected):
        if isinstance(expected, tuple) and expected[-1] is NULL_OR_EMPTY:
            if result[-1] in ({}, None):
                expected = (*expected[:-1], result[-1])
        self.assertEqual(result, expected)

    def test_fastcall(self):
        # Test _PyObject_FastCall()

        for func, args, expected in self.CALLS_POSARGS:
            with self.subTest(func=func, args=args):
                result = _testcapi.pyobject_fastcall(func, args)
                self.check_result(result, expected)

                if not args:
                    # args=NULL, nargs=0
                    result = _testcapi.pyobject_fastcall(func, None)
                    self.check_result(result, expected)

    def test_vectorcall_dict(self):
        # Test PyObject_VectorcallDict()

        for func, args, expected in self.CALLS_POSARGS:
            with self.subTest(func=func, args=args):
                # kwargs=NULL
                result = _testcapi.pyobject_fastcalldict(func, args, None)
                self.check_result(result, expected)

                if not args:
                    # args=NULL, nargs=0, kwargs=NULL
                    result = _testcapi.pyobject_fastcalldict(func, None, None)
                    self.check_result(result, expected)

        for func, args, kwargs, expected in self.CALLS_KWARGS:
            with self.subTest(func=func, args=args, kwargs=kwargs):
                result = _testcapi.pyobject_fastcalldict(func, args, kwargs)
                self.check_result(result, expected)

    def test_vectorcall(self):
        # Test PyObject_Vectorcall()

        for func, args, expected in self.CALLS_POSARGS:
            with self.subTest(func=func, args=args):
                # kwnames=NULL
                result = _testcapi.pyobject_vectorcall(func, args, None)
                self.check_result(result, expected)

                # kwnames=()
                result = _testcapi.pyobject_vectorcall(func, args, ())
                self.check_result(result, expected)

                if not args:
                    # kwnames=NULL
                    result = _testcapi.pyobject_vectorcall(func, None, None)
                    self.check_result(result, expected)

                    # kwnames=()
                    result = _testcapi.pyobject_vectorcall(func, None, ())
                    self.check_result(result, expected)

        for func, args, kwargs, expected in self.CALLS_KWARGS:
            with self.subTest(func=func, args=args, kwargs=kwargs):
                kwnames = tuple(kwargs.keys())
                args = args + tuple(kwargs.values())
                result = _testcapi.pyobject_vectorcall(func, args, kwnames)
                self.check_result(result, expected)

    def test_fastcall_clearing_dict(self):
        # Test bpo-36907: the point of the test is just checking that this
        # does not crash.
        class IntWithDict:
            __slots__ = ["kwargs"]

            def __init__(self, **kwargs):
                self.kwargs = kwargs

            def __index__(self):
                self.kwargs.clear()
                gc.collect()
                return 0

        x = IntWithDict(dont_inherit=IntWithDict())
        # We test the argument handling of "compile" here, the compilation
        # itself is not relevant. When we pass flags=x below, x.__index__() is
        # called, which changes the keywords dict.
        compile("pass", "", "exec", x, **x.kwargs)
Ejemplo n.º 2
0
 def setUp(self):
     self.obj = _testcapi.MethClass()
     self.expected_self = _testcapi.MethClass