mirror of https://github.com/swig/swig.git
102 lines
4.8 KiB
Python
102 lines
4.8 KiB
Python
import doxygen_constructors
|
|
import inspect
|
|
import comment_verifier
|
|
from swig_test_utils import swig_check, swig_assert_raises
|
|
|
|
|
|
def check_ab(obj, a, b):
|
|
"""Assert the constructor stored a and b correctly on the C++ instance."""
|
|
swig_check(obj.get_a(), a)
|
|
swig_check(obj.get_b(), b)
|
|
|
|
|
|
# Constructor docstrings reach __init__ in both -builtin and non-builtin.
|
|
comment_verifier.check(inspect.getdoc(doxygen_constructors.ClassZeroArg.__init__), "Zero arg ctor doc.")
|
|
comment_verifier.check(inspect.getdoc(doxygen_constructors.ClassOneArg.__init__), "One arg ctor doc.")
|
|
comment_verifier.check(inspect.getdoc(doxygen_constructors.ClassTwoArg.__init__), "Two arg ctor doc.")
|
|
comment_verifier.check(inspect.getdoc(doxygen_constructors.ClassKwArg.__init__), "Kwarg ctor doc.")
|
|
|
|
# Positional construction; argument values reach the C++ members.
|
|
doxygen_constructors.ClassZeroArg()
|
|
swig_check(doxygen_constructors.ClassOneArg(11).get_a(), 11)
|
|
check_ab(doxygen_constructors.ClassTwoArg(11, 2.5), 11, 2.5)
|
|
check_ab(doxygen_constructors.ClassKwArg(11, 22), 11, 22)
|
|
|
|
# Wrong arg count raises TypeError, identical between wrapping modes.
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassZeroArg(1)
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassOneArg()
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassOneArg(1, 2)
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassTwoArg(1)
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassTwoArg(1, 2.0, 3)
|
|
|
|
# Without %feature("kwargs"), kwarg handling diverges between wrapping modes (long standing SWIG
|
|
# behaviour, not specific to doxygen): the non-builtin shadow __init__ is a Python function and
|
|
# accepts kwargs natively, while the builtin tp_init C wrapper only accepts positional args. The
|
|
# %feature("kwargs") path below is where the two modes are expected to behave identically.
|
|
|
|
# With %feature("kwargs"), kwarg order does not affect which value binds to which name,
|
|
# and mixed positional/kwarg works.
|
|
check_ab(doxygen_constructors.ClassKwArg(a=11, b=22), 11, 22)
|
|
check_ab(doxygen_constructors.ClassKwArg(b=22, a=11), 11, 22) # reversed kwarg order
|
|
check_ab(doxygen_constructors.ClassKwArg(11, b=22), 11, 22) # mixed positional + kwarg
|
|
|
|
# Unknown kwarg name and missing arg both raise TypeError, identical in both modes.
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassKwArg(c=1, d=2)
|
|
with swig_assert_raises(TypeError):
|
|
doxygen_constructors.ClassKwArg(a=1)
|
|
|
|
# Direct instantiation Cls(args) calls the tp_init slot and bypasses tp_dict["__init__"]
|
|
# altogether, so it does not exercise the descriptor SWIG installs under -builtin to carry
|
|
# the doxygen docstring. super().__init__() from a Python subclass is the realistic path
|
|
# that does an attribute lookup and lands on that descriptor. The subclasses below run
|
|
# that path for positional and keyword args.
|
|
class TwoArgSubclass(doxygen_constructors.ClassTwoArg):
|
|
def __init__(self, a, b, label):
|
|
super().__init__(a, b)
|
|
self.label = label
|
|
|
|
class KwArgSubclass(doxygen_constructors.ClassKwArg):
|
|
def __init__(self, **kw):
|
|
super().__init__(**kw)
|
|
|
|
sub = TwoArgSubclass(11, 2.5, "subclass-tag")
|
|
swig_check(sub.get_a(), 11)
|
|
swig_check(sub.get_b(), 2.5)
|
|
swig_check(sub.label, "subclass-tag")
|
|
|
|
check_ab(KwArgSubclass(a=11, b=22), 11, 22)
|
|
check_ab(KwArgSubclass(b=22, a=11), 11, 22)
|
|
|
|
# Confirm the descriptor SWIG installed actually replaced CPython's auto-installed slot
|
|
# wrapper. PyType_Ready's add_operators populates tp_dict["__init__"] with a
|
|
# wrapper_descriptor (type(object.__init__)) whose docstring is the fixed generic
|
|
# "Initialize self." string. PyDescr_NewMethod produces a method_descriptor
|
|
# (type(list.append)). Combined with the docstring checks above and the super().__init__()
|
|
# calls above, this proves the wrapper is the call path under -builtin and not the default
|
|
# slot wrapper.
|
|
# Note:
|
|
# >>> type(list.append).__name__
|
|
# 'method_descriptor'
|
|
# >>> type(object.__init__).__name__
|
|
# 'wrapper_descriptor'
|
|
if doxygen_constructors.is_python_builtin():
|
|
swig_check(type(doxygen_constructors.ClassZeroArg.__init__).__name__, "method_descriptor")
|
|
swig_check(type(doxygen_constructors.ClassTwoArg.__init__).__name__, "method_descriptor")
|
|
swig_check(type(doxygen_constructors.ClassKwArg.__init__).__name__, "method_descriptor")
|
|
|
|
# Control: ClassNoDoc has no doxygen comment on its constructor, so SWIG does not emit
|
|
# the __init__ descriptor override and PyType_Ready's auto-installed slot wrapper stays
|
|
# in place. Its descriptor type matches type(object.__init__) and differs from the
|
|
# documented classes above.
|
|
swig_check(type(doxygen_constructors.ClassNoDoc.__init__).__name__, "wrapper_descriptor")
|
|
swig_check(type(doxygen_constructors.ClassNoDoc.__init__), type(object.__init__))
|
|
|
|
swig_check(type(doxygen_constructors.ClassKwArg.__init__), type(list.append))
|
|
|