Blame external/pybind11/tests/test_virtual_functions.py

Packit 534379
import pytest
Packit 534379
Packit 534379
from pybind11_tests import virtual_functions as m
Packit 534379
from pybind11_tests import ConstructorStats
Packit 534379
Packit 534379
Packit 534379
def test_override(capture, msg):
Packit 534379
    class ExtendedExampleVirt(m.ExampleVirt):
Packit 534379
        def __init__(self, state):
Packit 534379
            super(ExtendedExampleVirt, self).__init__(state + 1)
Packit 534379
            self.data = "Hello world"
Packit 534379
Packit 534379
        def run(self, value):
Packit 534379
            print('ExtendedExampleVirt::run(%i), calling parent..' % value)
Packit 534379
            return super(ExtendedExampleVirt, self).run(value + 1)
Packit 534379
Packit 534379
        def run_bool(self):
Packit 534379
            print('ExtendedExampleVirt::run_bool()')
Packit 534379
            return False
Packit 534379
Packit 534379
        def get_string1(self):
Packit 534379
            return "override1"
Packit 534379
Packit 534379
        def pure_virtual(self):
Packit 534379
            print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)
Packit 534379
Packit 534379
    class ExtendedExampleVirt2(ExtendedExampleVirt):
Packit 534379
        def __init__(self, state):
Packit 534379
            super(ExtendedExampleVirt2, self).__init__(state + 1)
Packit 534379
Packit 534379
        def get_string2(self):
Packit 534379
            return "override2"
Packit 534379
Packit 534379
    ex12 = m.ExampleVirt(10)
Packit 534379
    with capture:
Packit 534379
        assert m.runExampleVirt(ex12, 20) == 30
Packit 534379
    assert capture == """
Packit 534379
        Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
Packit 534379
    """  # noqa: E501 line too long
Packit 534379
Packit 534379
    with pytest.raises(RuntimeError) as excinfo:
Packit 534379
        m.runExampleVirtVirtual(ex12)
Packit 534379
    assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'
Packit 534379
Packit 534379
    ex12p = ExtendedExampleVirt(10)
Packit 534379
    with capture:
Packit 534379
        assert m.runExampleVirt(ex12p, 20) == 32
Packit 534379
    assert capture == """
Packit 534379
        ExtendedExampleVirt::run(20), calling parent..
Packit 534379
        Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
Packit 534379
    """  # noqa: E501 line too long
Packit 534379
    with capture:
Packit 534379
        assert m.runExampleVirtBool(ex12p) is False
Packit 534379
    assert capture == "ExtendedExampleVirt::run_bool()"
Packit 534379
    with capture:
Packit 534379
        m.runExampleVirtVirtual(ex12p)
Packit 534379
    assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"
Packit 534379
Packit 534379
    ex12p2 = ExtendedExampleVirt2(15)
Packit 534379
    with capture:
Packit 534379
        assert m.runExampleVirt(ex12p2, 50) == 68
Packit 534379
    assert capture == """
Packit 534379
        ExtendedExampleVirt::run(50), calling parent..
Packit 534379
        Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
Packit 534379
    """  # noqa: E501 line too long
Packit 534379
Packit 534379
    cstats = ConstructorStats.get(m.ExampleVirt)
Packit 534379
    assert cstats.alive() == 3
Packit 534379
    del ex12, ex12p, ex12p2
Packit 534379
    assert cstats.alive() == 0
Packit 534379
    assert cstats.values() == ['10', '11', '17']
Packit 534379
    assert cstats.copy_constructions == 0
Packit 534379
    assert cstats.move_constructions >= 0
Packit 534379
Packit 534379
Packit 534379
def test_alias_delay_initialization1(capture):
Packit 534379
    """`A` only initializes its trampoline class when we inherit from it
Packit 534379
Packit 534379
    If we just create and use an A instance directly, the trampoline initialization is
Packit 534379
    bypassed and we only initialize an A() instead (for performance reasons).
Packit 534379
    """
Packit 534379
    class B(m.A):
Packit 534379
        def __init__(self):
Packit 534379
            super(B, self).__init__()
Packit 534379
Packit 534379
        def f(self):
Packit 534379
            print("In python f()")
Packit 534379
Packit 534379
    # C++ version
Packit 534379
    with capture:
Packit 534379
        a = m.A()
Packit 534379
        m.call_f(a)
Packit 534379
        del a
Packit 534379
        pytest.gc_collect()
Packit 534379
    assert capture == "A.f()"
Packit 534379
Packit 534379
    # Python version
Packit 534379
    with capture:
Packit 534379
        b = B()
Packit 534379
        m.call_f(b)
Packit 534379
        del b
Packit 534379
        pytest.gc_collect()
Packit 534379
    assert capture == """
Packit 534379
        PyA.PyA()
Packit 534379
        PyA.f()
Packit 534379
        In python f()
Packit 534379
        PyA.~PyA()
Packit 534379
    """
Packit 534379
Packit 534379
Packit 534379
def test_alias_delay_initialization2(capture):
Packit 534379
    """`A2`, unlike the above, is configured to always initialize the alias
Packit 534379
Packit 534379
    While the extra initialization and extra class layer has small virtual dispatch
Packit 534379
    performance penalty, it also allows us to do more things with the trampoline
Packit 534379
    class such as defining local variables and performing construction/destruction.
Packit 534379
    """
Packit 534379
    class B2(m.A2):
Packit 534379
        def __init__(self):
Packit 534379
            super(B2, self).__init__()
Packit 534379
Packit 534379
        def f(self):
Packit 534379
            print("In python B2.f()")
Packit 534379
Packit 534379
    # No python subclass version
Packit 534379
    with capture:
Packit 534379
        a2 = m.A2()
Packit 534379
        m.call_f(a2)
Packit 534379
        del a2
Packit 534379
        pytest.gc_collect()
Packit 534379
        a3 = m.A2(1)
Packit 534379
        m.call_f(a3)
Packit 534379
        del a3
Packit 534379
        pytest.gc_collect()
Packit 534379
    assert capture == """
Packit 534379
        PyA2.PyA2()
Packit 534379
        PyA2.f()
Packit 534379
        A2.f()
Packit 534379
        PyA2.~PyA2()
Packit 534379
        PyA2.PyA2()
Packit 534379
        PyA2.f()
Packit 534379
        A2.f()
Packit 534379
        PyA2.~PyA2()
Packit 534379
    """
Packit 534379
Packit 534379
    # Python subclass version
Packit 534379
    with capture:
Packit 534379
        b2 = B2()
Packit 534379
        m.call_f(b2)
Packit 534379
        del b2
Packit 534379
        pytest.gc_collect()
Packit 534379
    assert capture == """
Packit 534379
        PyA2.PyA2()
Packit 534379
        PyA2.f()
Packit 534379
        In python B2.f()
Packit 534379
        PyA2.~PyA2()
Packit 534379
    """
Packit 534379
Packit 534379
Packit 534379
# PyPy: Reference count > 1 causes call with noncopyable instance
Packit 534379
# to fail in ncv1.print_nc()
Packit 534379
@pytest.unsupported_on_pypy
Packit 534379
@pytest.mark.skipif(not hasattr(m, "NCVirt"), reason="NCVirt test broken on ICPC")
Packit 534379
def test_move_support():
Packit 534379
    class NCVirtExt(m.NCVirt):
Packit 534379
        def get_noncopyable(self, a, b):
Packit 534379
            # Constructs and returns a new instance:
Packit 534379
            nc = m.NonCopyable(a * a, b * b)
Packit 534379
            return nc
Packit 534379
Packit 534379
        def get_movable(self, a, b):
Packit 534379
            # Return a referenced copy
Packit 534379
            self.movable = m.Movable(a, b)
Packit 534379
            return self.movable
Packit 534379
Packit 534379
    class NCVirtExt2(m.NCVirt):
Packit 534379
        def get_noncopyable(self, a, b):
Packit 534379
            # Keep a reference: this is going to throw an exception
Packit 534379
            self.nc = m.NonCopyable(a, b)
Packit 534379
            return self.nc
Packit 534379
Packit 534379
        def get_movable(self, a, b):
Packit 534379
            # Return a new instance without storing it
Packit 534379
            return m.Movable(a, b)
Packit 534379
Packit 534379
    ncv1 = NCVirtExt()
Packit 534379
    assert ncv1.print_nc(2, 3) == "36"
Packit 534379
    assert ncv1.print_movable(4, 5) == "9"
Packit 534379
    ncv2 = NCVirtExt2()
Packit 534379
    assert ncv2.print_movable(7, 7) == "14"
Packit 534379
    # Don't check the exception message here because it differs under debug/non-debug mode
Packit 534379
    with pytest.raises(RuntimeError):
Packit 534379
        ncv2.print_nc(9, 9)
Packit 534379
Packit 534379
    nc_stats = ConstructorStats.get(m.NonCopyable)
Packit 534379
    mv_stats = ConstructorStats.get(m.Movable)
Packit 534379
    assert nc_stats.alive() == 1
Packit 534379
    assert mv_stats.alive() == 1
Packit 534379
    del ncv1, ncv2
Packit 534379
    assert nc_stats.alive() == 0
Packit 534379
    assert mv_stats.alive() == 0
Packit 534379
    assert nc_stats.values() == ['4', '9', '9', '9']
Packit 534379
    assert mv_stats.values() == ['4', '5', '7', '7']
Packit 534379
    assert nc_stats.copy_constructions == 0
Packit 534379
    assert mv_stats.copy_constructions == 1
Packit 534379
    assert nc_stats.move_constructions >= 0
Packit 534379
    assert mv_stats.move_constructions >= 0
Packit 534379
Packit 534379
Packit 534379
def test_dispatch_issue(msg):
Packit 534379
    """#159: virtual function dispatch has problems with similar-named functions"""
Packit 534379
    class PyClass1(m.DispatchIssue):
Packit 534379
        def dispatch(self):
Packit 534379
            return "Yay.."
Packit 534379
Packit 534379
    class PyClass2(m.DispatchIssue):
Packit 534379
        def dispatch(self):
Packit 534379
            with pytest.raises(RuntimeError) as excinfo:
Packit 534379
                super(PyClass2, self).dispatch()
Packit 534379
            assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'
Packit 534379
Packit 534379
            p = PyClass1()
Packit 534379
            return m.dispatch_issue_go(p)
Packit 534379
Packit 534379
    b = PyClass2()
Packit 534379
    assert m.dispatch_issue_go(b) == "Yay.."
Packit 534379
Packit 534379
Packit 534379
def test_override_ref():
Packit 534379
    """#392/397: overriding reference-returning functions"""
Packit 534379
    o = m.OverrideTest("asdf")
Packit 534379
Packit 534379
    # Not allowed (see associated .cpp comment)
Packit 534379
    # i = o.str_ref()
Packit 534379
    # assert o.str_ref() == "asdf"
Packit 534379
    assert o.str_value() == "asdf"
Packit 534379
Packit 534379
    assert o.A_value().value == "hi"
Packit 534379
    a = o.A_ref()
Packit 534379
    assert a.value == "hi"
Packit 534379
    a.value = "bye"
Packit 534379
    assert a.value == "bye"
Packit 534379
Packit 534379
Packit 534379
def test_inherited_virtuals():
Packit 534379
    class AR(m.A_Repeat):
Packit 534379
        def unlucky_number(self):
Packit 534379
            return 99
Packit 534379
Packit 534379
    class AT(m.A_Tpl):
Packit 534379
        def unlucky_number(self):
Packit 534379
            return 999
Packit 534379
Packit 534379
    obj = AR()
Packit 534379
    assert obj.say_something(3) == "hihihi"
Packit 534379
    assert obj.unlucky_number() == 99
Packit 534379
    assert obj.say_everything() == "hi 99"
Packit 534379
Packit 534379
    obj = AT()
Packit 534379
    assert obj.say_something(3) == "hihihi"
Packit 534379
    assert obj.unlucky_number() == 999
Packit 534379
    assert obj.say_everything() == "hi 999"
Packit 534379
Packit 534379
    for obj in [m.B_Repeat(), m.B_Tpl()]:
Packit 534379
        assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
        assert obj.unlucky_number() == 13
Packit 534379
        assert obj.lucky_number() == 7.0
Packit 534379
        assert obj.say_everything() == "B says hi 1 times 13"
Packit 534379
Packit 534379
    for obj in [m.C_Repeat(), m.C_Tpl()]:
Packit 534379
        assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
        assert obj.unlucky_number() == 4444
Packit 534379
        assert obj.lucky_number() == 888.0
Packit 534379
        assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    class CR(m.C_Repeat):
Packit 534379
        def lucky_number(self):
Packit 534379
            return m.C_Repeat.lucky_number(self) + 1.25
Packit 534379
Packit 534379
    obj = CR()
Packit 534379
    assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
    assert obj.unlucky_number() == 4444
Packit 534379
    assert obj.lucky_number() == 889.25
Packit 534379
    assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    class CT(m.C_Tpl):
Packit 534379
        pass
Packit 534379
Packit 534379
    obj = CT()
Packit 534379
    assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
    assert obj.unlucky_number() == 4444
Packit 534379
    assert obj.lucky_number() == 888.0
Packit 534379
    assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    class CCR(CR):
Packit 534379
        def lucky_number(self):
Packit 534379
            return CR.lucky_number(self) * 10
Packit 534379
Packit 534379
    obj = CCR()
Packit 534379
    assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
    assert obj.unlucky_number() == 4444
Packit 534379
    assert obj.lucky_number() == 8892.5
Packit 534379
    assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    class CCT(CT):
Packit 534379
        def lucky_number(self):
Packit 534379
            return CT.lucky_number(self) * 1000
Packit 534379
Packit 534379
    obj = CCT()
Packit 534379
    assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
    assert obj.unlucky_number() == 4444
Packit 534379
    assert obj.lucky_number() == 888000.0
Packit 534379
    assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    class DR(m.D_Repeat):
Packit 534379
        def unlucky_number(self):
Packit 534379
            return 123
Packit 534379
Packit 534379
        def lucky_number(self):
Packit 534379
            return 42.0
Packit 534379
Packit 534379
    for obj in [m.D_Repeat(), m.D_Tpl()]:
Packit 534379
        assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
        assert obj.unlucky_number() == 4444
Packit 534379
        assert obj.lucky_number() == 888.0
Packit 534379
        assert obj.say_everything() == "B says hi 1 times 4444"
Packit 534379
Packit 534379
    obj = DR()
Packit 534379
    assert obj.say_something(3) == "B says hi 3 times"
Packit 534379
    assert obj.unlucky_number() == 123
Packit 534379
    assert obj.lucky_number() == 42.0
Packit 534379
    assert obj.say_everything() == "B says hi 1 times 123"
Packit 534379
Packit 534379
    class DT(m.D_Tpl):
Packit 534379
        def say_something(self, times):
Packit 534379
            return "DT says:" + (' quack' * times)
Packit 534379
Packit 534379
        def unlucky_number(self):
Packit 534379
            return 1234
Packit 534379
Packit 534379
        def lucky_number(self):
Packit 534379
            return -4.25
Packit 534379
Packit 534379
    obj = DT()
Packit 534379
    assert obj.say_something(3) == "DT says: quack quack quack"
Packit 534379
    assert obj.unlucky_number() == 1234
Packit 534379
    assert obj.lucky_number() == -4.25
Packit 534379
    assert obj.say_everything() == "DT says: quack 1234"
Packit 534379
Packit 534379
    class DT2(DT):
Packit 534379
        def say_something(self, times):
Packit 534379
            return "DT2: " + ('QUACK' * times)
Packit 534379
Packit 534379
        def unlucky_number(self):
Packit 534379
            return -3
Packit 534379
Packit 534379
    class BT(m.B_Tpl):
Packit 534379
        def say_something(self, times):
Packit 534379
            return "BT" * times
Packit 534379
Packit 534379
        def unlucky_number(self):
Packit 534379
            return -7
Packit 534379
Packit 534379
        def lucky_number(self):
Packit 534379
            return -1.375
Packit 534379
Packit 534379
    obj = BT()
Packit 534379
    assert obj.say_something(3) == "BTBTBT"
Packit 534379
    assert obj.unlucky_number() == -7
Packit 534379
    assert obj.lucky_number() == -1.375
Packit 534379
    assert obj.say_everything() == "BT -7"
Packit 534379
Packit 534379
Packit 534379
def test_issue_1454():
Packit 534379
    # Fix issue #1454 (crash when acquiring/releasing GIL on another thread in Python 2.7)
Packit 534379
    m.test_gil()
Packit 534379
    m.test_gil_from_thread()