swig/Examples/test-suite/python/doxygen_constructors_runme.py

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))