Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 240 additions & 0 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ def test_directive_output_invalid_command(self):
- 'impl_prototype'
- 'parser_prototype'
- 'parser_definition'
- 'vectorcall_definition'
- 'cpp_endif'
- 'methoddef_ifndef'
- 'impl_definition'
Expand Down Expand Up @@ -2496,6 +2497,162 @@ def test_duplicate_coexist(self):
"""
self.expect_failure(block, err, lineno=2)

def test_duplicate_vectorcall(self):
err = "Called @vectorcall twice"
block = """
module m
class Foo "FooObject *" ""
@vectorcall
@vectorcall
Foo.__init__
"""
self.expect_failure(block, err, lineno=3)

def test_vectorcall_on_regular_method(self):
err = "@vectorcall can only be used with __init__ and __new__ methods"
block = """
module m
class Foo "FooObject *" ""
@vectorcall
Foo.some_method
"""
self.expect_failure(block, err, lineno=3)

def test_vectorcall_on_module_function(self):
err = "@vectorcall can only be used with __init__ and __new__ methods"
block = """
module m
@vectorcall
m.fn
"""
self.expect_failure(block, err, lineno=2)

def test_vectorcall_on_init(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@vectorcall
Foo.__init__
iterable: object = NULL
/
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)
self.assertFalse(func.vectorcall_exact_only)

def test_vectorcall_on_new(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@classmethod
@vectorcall
Foo.__new__
x: object = NULL
/
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)
self.assertFalse(func.vectorcall_exact_only)

def test_vectorcall_exact_only(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@vectorcall exact_only
Foo.__init__
iterable: object = NULL
/
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)
self.assertTrue(func.vectorcall_exact_only)

def test_vectorcall_init_with_kwargs(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@vectorcall
Foo.__init__
source: object = NULL
encoding: str = NULL
errors: str = NULL
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)

def test_vectorcall_new_with_kwargs(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@classmethod
@vectorcall
Foo.__new__
source: object = NULL
*
encoding: str = NULL
errors: str = NULL
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)

def test_vectorcall_init_no_args(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@vectorcall
Foo.__init__
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)

def test_vectorcall_zero_arg(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@classmethod
@vectorcall zero_arg=_PyFoo_GetEmpty()
Foo.__new__
x: object = NULL
/
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)
self.assertFalse(func.vectorcall_exact_only)
self.assertEqual(func.vectorcall_zero_arg, '_PyFoo_GetEmpty()')

def test_vectorcall_zero_arg_with_exact(self):
block = """
module m
class Foo "FooObject *" "Foo_Type"
@classmethod
@vectorcall exact_only zero_arg=get_cached()
Foo.__new__
x: object = NULL
/
"""
func = self.parse_function(block, signatures_in_block=3,
function_index=2)
self.assertTrue(func.vectorcall)
self.assertTrue(func.vectorcall_exact_only)
self.assertEqual(func.vectorcall_zero_arg, 'get_cached()')

def test_vectorcall_invalid_kwarg(self):
err = "unknown argument"
block = """
module m
class Foo "FooObject *" ""
@vectorcall bogus=True
Foo.__init__
"""
self.expect_failure(block, err, lineno=2)

def test_unused_param(self):
block = self.parse("""
module foo
Expand Down Expand Up @@ -4136,6 +4293,89 @@ def test_kwds_with_pos_only_and_stararg(self):
self.assertEqual(ac_tester.kwds_with_pos_only_and_stararg(1, 2, *args, **kwds), (1, 2, args, kwds))


@unittest.skipIf(ac_tester is None, "_testclinic is missing")
class VectorcallFunctionalTest(unittest.TestCase):
"""Runtime tests for @vectorcall exemplar types."""

def test_vc_new_no_args(self):
obj = ac_tester.VcNew()
self.assertIsInstance(obj, ac_tester.VcNew)

def test_vc_new_with_arg(self):
obj = ac_tester.VcNew(1)
self.assertIsInstance(obj, ac_tester.VcNew)

def test_vc_new_with_kwarg(self):
obj = ac_tester.VcNew(a=1)
self.assertIsInstance(obj, ac_tester.VcNew)

def test_vc_new_rejects_extra_args(self):
with self.assertRaises(TypeError):
ac_tester.VcNew(1, 2)

def test_vc_init_required_pos_only(self):
obj = ac_tester.VcInit(1)
self.assertIsInstance(obj, ac_tester.VcInit)

def test_vc_init_with_keyword(self):
obj = ac_tester.VcInit(1, b=2)
self.assertIsInstance(obj, ac_tester.VcInit)

def test_vc_init_with_positional_optional(self):
obj = ac_tester.VcInit(1, 2)
self.assertIsInstance(obj, ac_tester.VcInit)

def test_vc_init_missing_required(self):
with self.assertRaises(TypeError):
ac_tester.VcInit()

def test_vc_init_rejects_a_as_keyword(self):
# 'a' is positional-only
with self.assertRaises(TypeError):
ac_tester.VcInit(a=1)

def test_vc_new_exact_one_arg(self):
obj = ac_tester.VcNewExact(1)
self.assertIsInstance(obj, ac_tester.VcNewExact)

def test_vc_new_exact_two_args(self):
obj = ac_tester.VcNewExact(1, 2)
self.assertIsInstance(obj, ac_tester.VcNewExact)

def test_vc_new_exact_missing_required(self):
with self.assertRaises(TypeError):
ac_tester.VcNewExact()

def test_vc_new_exact_subclass(self):
# exact_only: subclass goes through non-vectorcall (tp_new) path
Sub = type('Sub', (ac_tester.VcNewExact,), {})
obj = Sub(1)
self.assertIsInstance(obj, Sub)
self.assertIsInstance(obj, ac_tester.VcNewExact)

def test_vc_new_zeroarg_no_args(self):
# zero_arg returns Py_None when called with no arguments
result = ac_tester.VcNewZeroArg()
self.assertIs(result, None)

def test_vc_new_zeroarg_with_pos(self):
obj = ac_tester.VcNewZeroArg(1)
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)

def test_vc_new_zeroarg_with_kwonly(self):
obj = ac_tester.VcNewZeroArg(b=2)
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)

def test_vc_new_zeroarg_with_both(self):
obj = ac_tester.VcNewZeroArg(1, b=2)
self.assertIsInstance(obj, ac_tester.VcNewZeroArg)

def test_vc_new_zeroarg_rejects_a_as_keyword(self):
# 'a' is positional-only
with self.assertRaises(TypeError):
ac_tester.VcNewZeroArg(a=1)


class LimitedCAPIOutputTests(unittest.TestCase):

def setUp(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add a ``@vectorcall`` decorator to Argument Clinic that can be used on
``__init__`` and ``__new__`` which generates :ref:`vectorcall` argument
parsing.
Loading