test_virtual_functions.py 9.3 KB
Newer Older
Dean Moldovan's avatar
Dean Moldovan committed
1
import pytest
2
import pybind11_tests
Dean Moldovan's avatar
Dean Moldovan committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pybind11_tests import ConstructorStats


def test_override(capture, msg):
    from pybind11_tests import (ExampleVirt, runExampleVirt, runExampleVirtVirtual,
                                runExampleVirtBool)

    class ExtendedExampleVirt(ExampleVirt):
        def __init__(self, state):
            super(ExtendedExampleVirt, self).__init__(state + 1)
            self.data = "Hello world"

        def run(self, value):
            print('ExtendedExampleVirt::run(%i), calling parent..' % value)
            return super(ExtendedExampleVirt, self).run(value + 1)

        def run_bool(self):
            print('ExtendedExampleVirt::run_bool()')
            return False

23
24
25
        def get_string1(self):
            return "override1"

Dean Moldovan's avatar
Dean Moldovan committed
26
27
28
        def pure_virtual(self):
            print('ExtendedExampleVirt::pure_virtual(): %s' % self.data)

29
30
31
32
33
34
35
    class ExtendedExampleVirt2(ExtendedExampleVirt):
        def __init__(self, state):
            super(ExtendedExampleVirt2, self).__init__(state + 1)

        def get_string2(self):
            return "override2"

Dean Moldovan's avatar
Dean Moldovan committed
36
37
38
    ex12 = ExampleVirt(10)
    with capture:
        assert runExampleVirt(ex12, 20) == 30
39
40
41
    assert capture == """
        Original implementation of ExampleVirt::run(state=10, value=20, str1=default1, str2=default2)
    """  # noqa: E501 line too long
Dean Moldovan's avatar
Dean Moldovan committed
42
43
44
45
46
47
48
49
50
51

    with pytest.raises(RuntimeError) as excinfo:
        runExampleVirtVirtual(ex12)
    assert msg(excinfo.value) == 'Tried to call pure virtual function "ExampleVirt::pure_virtual"'

    ex12p = ExtendedExampleVirt(10)
    with capture:
        assert runExampleVirt(ex12p, 20) == 32
    assert capture == """
        ExtendedExampleVirt::run(20), calling parent..
52
        Original implementation of ExampleVirt::run(state=11, value=21, str1=override1, str2=default2)
53
    """  # noqa: E501 line too long
Dean Moldovan's avatar
Dean Moldovan committed
54
55
56
57
58
59
60
    with capture:
        assert runExampleVirtBool(ex12p) is False
    assert capture == "ExtendedExampleVirt::run_bool()"
    with capture:
        runExampleVirtVirtual(ex12p)
    assert capture == "ExtendedExampleVirt::pure_virtual(): Hello world"

61
62
63
64
65
66
    ex12p2 = ExtendedExampleVirt2(15)
    with capture:
        assert runExampleVirt(ex12p2, 50) == 68
    assert capture == """
        ExtendedExampleVirt::run(50), calling parent..
        Original implementation of ExampleVirt::run(state=17, value=51, str1=override1, str2=override2)
67
    """  # noqa: E501 line too long
68

Dean Moldovan's avatar
Dean Moldovan committed
69
    cstats = ConstructorStats.get(ExampleVirt)
70
71
    assert cstats.alive() == 3
    del ex12, ex12p, ex12p2
Dean Moldovan's avatar
Dean Moldovan committed
72
    assert cstats.alive() == 0
73
    assert cstats.values() == ['10', '11', '17']
Dean Moldovan's avatar
Dean Moldovan committed
74
75
76
77
    assert cstats.copy_constructions == 0
    assert cstats.move_constructions >= 0


78
def test_inheriting_repeat():
Dean Moldovan's avatar
Dean Moldovan committed
79
80
    from pybind11_tests import A_Repeat, B_Repeat, C_Repeat, D_Repeat, A_Tpl, B_Tpl, C_Tpl, D_Tpl

81
    class AR(A_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
82
83
84
        def unlucky_number(self):
            return 99

85
    class AT(A_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
86
87
88
        def unlucky_number(self):
            return 999

89
    obj = AR()
90
    assert obj.say_something(3) == "hihihi"
Dean Moldovan's avatar
Dean Moldovan committed
91
    assert obj.unlucky_number() == 99
92
    assert obj.say_everything() == "hi 99"
Dean Moldovan's avatar
Dean Moldovan committed
93

94
    obj = AT()
95
    assert obj.say_something(3) == "hihihi"
Dean Moldovan's avatar
Dean Moldovan committed
96
    assert obj.unlucky_number() == 999
97
    assert obj.say_everything() == "hi 999"
Dean Moldovan's avatar
Dean Moldovan committed
98
99

    for obj in [B_Repeat(), B_Tpl()]:
100
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
101
102
        assert obj.unlucky_number() == 13
        assert obj.lucky_number() == 7.0
103
        assert obj.say_everything() == "B says hi 1 times 13"
Dean Moldovan's avatar
Dean Moldovan committed
104
105

    for obj in [C_Repeat(), C_Tpl()]:
106
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
107
108
        assert obj.unlucky_number() == 4444
        assert obj.lucky_number() == 888.0
109
        assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
110

111
    class CR(C_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
112
113
114
        def lucky_number(self):
            return C_Repeat.lucky_number(self) + 1.25

115
    obj = CR()
116
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
117
118
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 889.25
119
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
120

121
    class CT(C_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
122
123
        pass

124
    obj = CT()
125
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
126
127
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 888.0
128
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
129

130
    class CCR(CR):
Dean Moldovan's avatar
Dean Moldovan committed
131
        def lucky_number(self):
132
            return CR.lucky_number(self) * 10
Dean Moldovan's avatar
Dean Moldovan committed
133

134
    obj = CCR()
135
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
136
137
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 8892.5
138
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
139

140
    class CCT(CT):
Dean Moldovan's avatar
Dean Moldovan committed
141
        def lucky_number(self):
142
            return CT.lucky_number(self) * 1000
Dean Moldovan's avatar
Dean Moldovan committed
143

144
    obj = CCT()
145
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
146
147
    assert obj.unlucky_number() == 4444
    assert obj.lucky_number() == 888000.0
148
    assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
149

150
    class DR(D_Repeat):
Dean Moldovan's avatar
Dean Moldovan committed
151
152
153
154
155
156
157
        def unlucky_number(self):
            return 123

        def lucky_number(self):
            return 42.0

    for obj in [D_Repeat(), D_Tpl()]:
158
        assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
159
160
        assert obj.unlucky_number() == 4444
        assert obj.lucky_number() == 888.0
161
        assert obj.say_everything() == "B says hi 1 times 4444"
Dean Moldovan's avatar
Dean Moldovan committed
162

163
    obj = DR()
164
    assert obj.say_something(3) == "B says hi 3 times"
Dean Moldovan's avatar
Dean Moldovan committed
165
166
    assert obj.unlucky_number() == 123
    assert obj.lucky_number() == 42.0
167
    assert obj.say_everything() == "B says hi 1 times 123"
Dean Moldovan's avatar
Dean Moldovan committed
168

169
    class DT(D_Tpl):
Dean Moldovan's avatar
Dean Moldovan committed
170
        def say_something(self, times):
171
            return "DT says:" + (' quack' * times)
Dean Moldovan's avatar
Dean Moldovan committed
172
173
174
175
176
177
178

        def unlucky_number(self):
            return 1234

        def lucky_number(self):
            return -4.25

179
180
    obj = DT()
    assert obj.say_something(3) == "DT says: quack quack quack"
Dean Moldovan's avatar
Dean Moldovan committed
181
182
    assert obj.unlucky_number() == 1234
    assert obj.lucky_number() == -4.25
183
    assert obj.say_everything() == "DT says: quack 1234"
184

185
    class DT2(DT):
186
        def say_something(self, times):
187
            return "DT2: " + ('QUACK' * times)
188
189
190
191

        def unlucky_number(self):
            return -3

192
    class BT(B_Tpl):
193
        def say_something(self, times):
194
195
            return "BT" * times

196
197
        def unlucky_number(self):
            return -7
198

199
200
201
        def lucky_number(self):
            return -1.375

202
203
    obj = BT()
    assert obj.say_something(3) == "BTBTBT"
204
205
    assert obj.unlucky_number() == -7
    assert obj.lucky_number() == -1.375
206
207
    assert obj.say_everything() == "BT -7"

Dean Moldovan's avatar
Dean Moldovan committed
208

Wenzel Jakob's avatar
Wenzel Jakob committed
209
210
211
# PyPy: Reference count > 1 causes call with noncopyable instance
# to fail in ncv1.print_nc()
@pytest.unsupported_on_pypy
212
213
@pytest.mark.skipif(not hasattr(pybind11_tests, 'NCVirt'),
                    reason="NCVirt test broken on ICPC")
214
def test_move_support():
Dean Moldovan's avatar
Dean Moldovan committed
215
216
217
218
219
    from pybind11_tests import NCVirt, NonCopyable, Movable

    class NCVirtExt(NCVirt):
        def get_noncopyable(self, a, b):
            # Constructs and returns a new instance:
220
            nc = NonCopyable(a * a, b * b)
Dean Moldovan's avatar
Dean Moldovan committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
            return nc

        def get_movable(self, a, b):
            # Return a referenced copy
            self.movable = Movable(a, b)
            return self.movable

    class NCVirtExt2(NCVirt):
        def get_noncopyable(self, a, b):
            # Keep a reference: this is going to throw an exception
            self.nc = NonCopyable(a, b)
            return self.nc

        def get_movable(self, a, b):
            # Return a new instance without storing it
            return Movable(a, b)

    ncv1 = NCVirtExt()
239
240
    assert ncv1.print_nc(2, 3) == "36"
    assert ncv1.print_movable(4, 5) == "9"
Dean Moldovan's avatar
Dean Moldovan committed
241
    ncv2 = NCVirtExt2()
242
    assert ncv2.print_movable(7, 7) == "14"
Dean Moldovan's avatar
Dean Moldovan committed
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
    # Don't check the exception message here because it differs under debug/non-debug mode
    with pytest.raises(RuntimeError):
        ncv2.print_nc(9, 9)

    nc_stats = ConstructorStats.get(NonCopyable)
    mv_stats = ConstructorStats.get(Movable)
    assert nc_stats.alive() == 1
    assert mv_stats.alive() == 1
    del ncv1, ncv2
    assert nc_stats.alive() == 0
    assert mv_stats.alive() == 0
    assert nc_stats.values() == ['4', '9', '9', '9']
    assert mv_stats.values() == ['4', '5', '7', '7']
    assert nc_stats.copy_constructions == 0
    assert mv_stats.copy_constructions == 1
    assert nc_stats.move_constructions >= 0
    assert mv_stats.move_constructions >= 0
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298


def test_dispatch_issue(msg):
    """#159: virtual function dispatch has problems with similar-named functions"""
    from pybind11_tests import DispatchIssue, dispatch_issue_go

    class PyClass1(DispatchIssue):
        def dispatch(self):
            return "Yay.."

    class PyClass2(DispatchIssue):
        def dispatch(self):
            with pytest.raises(RuntimeError) as excinfo:
                super(PyClass2, self).dispatch()
            assert msg(excinfo.value) == 'Tried to call pure virtual function "Base::dispatch"'

            p = PyClass1()
            return dispatch_issue_go(p)

    b = PyClass2()
    assert dispatch_issue_go(b) == "Yay.."


def test_override_ref():
    """#392/397: overridding reference-returning functions"""
    from pybind11_tests import OverrideTest

    o = OverrideTest("asdf")

    # Not allowed (see associated .cpp comment)
    # i = o.str_ref()
    # assert o.str_ref() == "asdf"
    assert o.str_value() == "asdf"

    assert o.A_value().value == "hi"
    a = o.A_ref()
    assert a.value == "hi"
    a.value = "bye"
    assert a.value == "bye"