cppyabm  1.0.17
An agent-based library to integrate C++ and Python
test_class.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 import pytest
3 
4 import env # noqa: F401
5 
6 from pybind11_tests import class_ as m
7 from pybind11_tests import UserType, ConstructorStats
8 
9 
10 def test_repr():
11  # In Python 3.3+, repr() accesses __qualname__
12  assert "pybind11_type" in repr(type(UserType))
13  assert "UserType" in repr(UserType)
14 
15 
16 def test_instance(msg):
17  with pytest.raises(TypeError) as excinfo:
18  m.NoConstructor()
19  assert msg(excinfo.value) == "m.class_.NoConstructor: No constructor defined!"
20 
21  instance = m.NoConstructor.new_instance()
22 
23  cstats = ConstructorStats.get(m.NoConstructor)
24  assert cstats.alive() == 1
25  del instance
26  assert cstats.alive() == 0
27 
28 
29 def test_type():
30  assert m.check_type(1) == m.DerivedClass1
31  with pytest.raises(RuntimeError) as execinfo:
32  m.check_type(0)
33 
34  assert "pybind11::detail::get_type_info: unable to find type info" in str(
35  execinfo.value
36  )
37  assert "Invalid" in str(execinfo.value)
38 
39  # Currently not supported
40  # See https://github.com/pybind/pybind11/issues/2486
41  # assert m.check_type(2) == int
42 
43 
45  assert m.get_type_of(1) == int
46  assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
47  assert m.get_type_of(int) == type
48 
49 
51  assert m.get_type_classic(1) == int
52  assert m.get_type_classic(m.DerivedClass1()) == m.DerivedClass1
53  assert m.get_type_classic(int) == type
54 
55 
57  # If the above test deleted the class, this will segfault
58  assert m.get_type_of(m.DerivedClass1()) == m.DerivedClass1
59 
60 
62  assert m.as_type(int) == int
63 
64  with pytest.raises(TypeError):
65  assert m.as_type(1) == int
66 
67  with pytest.raises(TypeError):
68  assert m.as_type(m.DerivedClass1()) == m.DerivedClass1
69 
70 
71 def test_docstrings(doc):
72  assert doc(UserType) == "A `py::class_` type for testing"
73  assert UserType.__name__ == "UserType"
74  assert UserType.__module__ == "pybind11_tests"
75  assert UserType.get_value.__name__ == "get_value"
76  assert UserType.get_value.__module__ == "pybind11_tests"
77 
78  assert (
79  doc(UserType.get_value)
80  == """
81  get_value(self: m.UserType) -> int
82 
83  Get value using a method
84  """
85  )
86  assert doc(UserType.value) == "Get/set value using a property"
87 
88  assert (
89  doc(m.NoConstructor.new_instance)
90  == """
91  new_instance() -> m.class_.NoConstructor
92 
93  Return an instance
94  """
95  )
96 
97 
98 def test_qualname(doc):
99  """Tests that a properly qualified name is set in __qualname__ (even in pre-3.3, where we
100  backport the attribute) and that generated docstrings properly use it and the module name"""
101  assert m.NestBase.__qualname__ == "NestBase"
102  assert m.NestBase.Nested.__qualname__ == "NestBase.Nested"
103 
104  assert (
105  doc(m.NestBase.__init__)
106  == """
107  __init__(self: m.class_.NestBase) -> None
108  """
109  )
110  assert (
111  doc(m.NestBase.g)
112  == """
113  g(self: m.class_.NestBase, arg0: m.class_.NestBase.Nested) -> None
114  """
115  )
116  assert (
117  doc(m.NestBase.Nested.__init__)
118  == """
119  __init__(self: m.class_.NestBase.Nested) -> None
120  """
121  )
122  assert (
123  doc(m.NestBase.Nested.fn)
124  == """
125  fn(self: m.class_.NestBase.Nested, arg0: int, arg1: m.class_.NestBase, arg2: m.class_.NestBase.Nested) -> None
126  """ # noqa: E501 line too long
127  )
128  assert (
129  doc(m.NestBase.Nested.fa)
130  == """
131  fa(self: m.class_.NestBase.Nested, a: int, b: m.class_.NestBase, c: m.class_.NestBase.Nested) -> None
132  """ # noqa: E501 line too long
133  )
134  assert m.NestBase.__module__ == "pybind11_tests.class_"
135  assert m.NestBase.Nested.__module__ == "pybind11_tests.class_"
136 
137 
139  roger = m.Rabbit("Rabbit")
140  assert roger.name() + " is a " + roger.species() == "Rabbit is a parrot"
141  assert m.pet_name_species(roger) == "Rabbit is a parrot"
142 
143  polly = m.Pet("Polly", "parrot")
144  assert polly.name() + " is a " + polly.species() == "Polly is a parrot"
145  assert m.pet_name_species(polly) == "Polly is a parrot"
146 
147  molly = m.Dog("Molly")
148  assert molly.name() + " is a " + molly.species() == "Molly is a dog"
149  assert m.pet_name_species(molly) == "Molly is a dog"
150 
151  fred = m.Hamster("Fred")
152  assert fred.name() + " is a " + fred.species() == "Fred is a rodent"
153 
154  assert m.dog_bark(molly) == "Woof!"
155 
156  with pytest.raises(TypeError) as excinfo:
157  m.dog_bark(polly)
158  assert (
159  msg(excinfo.value)
160  == """
161  dog_bark(): incompatible function arguments. The following argument types are supported:
162  1. (arg0: m.class_.Dog) -> str
163 
164  Invoked with: <m.class_.Pet object at 0>
165  """
166  )
167 
168  with pytest.raises(TypeError) as excinfo:
169  m.Chimera("lion", "goat")
170  assert "No constructor defined!" in str(excinfo.value)
171 
172 
174 
175  # Single base
176  class Python(m.Pet):
177  def __init__(self):
178  pass
179 
180  with pytest.raises(TypeError) as exc_info:
181  Python()
182  expected = "m.class_.Pet.__init__() must be called when overriding __init__"
183  assert msg(exc_info.value) == expected
184 
185  # Multiple bases
186  class RabbitHamster(m.Rabbit, m.Hamster):
187  def __init__(self):
188  m.Rabbit.__init__(self, "RabbitHamster")
189 
190  with pytest.raises(TypeError) as exc_info:
191  RabbitHamster()
192  expected = "m.class_.Hamster.__init__() must be called when overriding __init__"
193  assert msg(exc_info.value) == expected
194 
195 
197  assert type(m.return_class_1()).__name__ == "DerivedClass1"
198  assert type(m.return_class_2()).__name__ == "DerivedClass2"
199  assert type(m.return_none()).__name__ == "NoneType"
200  # Repeat these a few times in a random order to ensure no invalid caching is applied
201  assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
202  assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
203  assert type(m.return_class_n(0)).__name__ == "BaseClass"
204  assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
205  assert type(m.return_class_n(2)).__name__ == "DerivedClass2"
206  assert type(m.return_class_n(0)).__name__ == "BaseClass"
207  assert type(m.return_class_n(1)).__name__ == "DerivedClass1"
208 
209 
211  objects = [tuple(), dict(), m.Pet("Polly", "parrot")] + [m.Dog("Molly")] * 4
212  expected = (True, True, True, True, True, False, False)
213  assert m.check_instances(objects) == expected
214 
215 
217  import re
218 
219  with pytest.raises(RuntimeError) as excinfo:
220  m.mismatched_holder_1()
221  assert re.match(
222  'generic_type: type ".*MismatchDerived1" does not have a non-default '
223  'holder type while its base ".*MismatchBase1" does',
224  str(excinfo.value),
225  )
226 
227  with pytest.raises(RuntimeError) as excinfo:
228  m.mismatched_holder_2()
229  assert re.match(
230  'generic_type: type ".*MismatchDerived2" has a non-default holder type '
231  'while its base ".*MismatchBase2" does not',
232  str(excinfo.value),
233  )
234 
235 
237  """#511: problem with inheritance + overwritten def_static"""
238  b = m.MyBase.make()
239  d1 = m.MyDerived.make2()
240  d2 = m.MyDerived.make()
241 
242  assert isinstance(b, m.MyBase)
243  assert isinstance(d1, m.MyDerived)
244  assert isinstance(d2, m.MyDerived)
245 
246 
248  """Ensure the lifetime of temporary objects created for implicit conversions"""
249  assert m.implicitly_convert_argument(UserType(5)) == 5
250  assert m.implicitly_convert_variable(UserType(5)) == 5
251 
252  assert "outside a bound function" in m.implicitly_convert_variable_fail(UserType(5))
253 
254 
256  """Tests that class-specific operator new/delete functions are invoked"""
257 
258  class SubAliased(m.AliasedHasOpNewDelSize):
259  pass
260 
261  with capture:
262  a = m.HasOpNewDel()
263  b = m.HasOpNewDelSize()
264  d = m.HasOpNewDelBoth()
265  assert (
266  capture
267  == """
268  A new 8
269  B new 4
270  D new 32
271  """
272  )
273  sz_alias = str(m.AliasedHasOpNewDelSize.size_alias)
274  sz_noalias = str(m.AliasedHasOpNewDelSize.size_noalias)
275  with capture:
276  c = m.AliasedHasOpNewDelSize()
277  c2 = SubAliased()
278  assert capture == ("C new " + sz_noalias + "\n" + "C new " + sz_alias + "\n")
279 
280  with capture:
281  del a
282  pytest.gc_collect()
283  del b
284  pytest.gc_collect()
285  del d
286  pytest.gc_collect()
287  assert (
288  capture
289  == """
290  A delete
291  B delete 4
292  D delete
293  """
294  )
295 
296  with capture:
297  del c
298  pytest.gc_collect()
299  del c2
300  pytest.gc_collect()
301  assert capture == ("C delete " + sz_noalias + "\n" + "C delete " + sz_alias + "\n")
302 
303 
305  """Expose protected member functions to Python using a helper class"""
306  a = m.ProtectedA()
307  assert a.foo() == 42
308 
309  b = m.ProtectedB()
310  assert b.foo() == 42
311 
312  class C(m.ProtectedB):
313  def __init__(self):
314  m.ProtectedB.__init__(self)
315 
316  def foo(self):
317  return 0
318 
319  c = C()
320  assert c.foo() == 0
321 
322 
324  """ Tests that simple POD classes can be constructed using C++11 brace initialization """
325  a = m.BraceInitialization(123, "test")
326  assert a.field1 == 123
327  assert a.field2 == "test"
328 
329  # Tests that a non-simple class doesn't get brace initialization (if the
330  # class defines an initializer_list constructor, in particular, it would
331  # win over the expected constructor).
332  b = m.NoBraceInitialization([123, 456])
333  assert b.vec == [123, 456]
334 
335 
336 @pytest.mark.xfail("env.PYPY")
338  """Instances must correctly increase/decrease the reference count of their types (#1029)"""
339  from sys import getrefcount
340 
341  class PyDog(m.Dog):
342  pass
343 
344  for cls in m.Dog, PyDog:
345  refcount_1 = getrefcount(cls)
346  molly = [cls("Molly") for _ in range(10)]
347  refcount_2 = getrefcount(cls)
348 
349  del molly
350  pytest.gc_collect()
351  refcount_3 = getrefcount(cls)
352 
353  assert refcount_1 == refcount_3
354  assert refcount_2 > refcount_1
355 
356 
358  # ensure that there is no runaway reentrant implicit conversion (#1035)
359  with pytest.raises(TypeError) as excinfo:
360  m.BogusImplicitConversion(0)
361  assert (
362  msg(excinfo.value)
363  == """
364  __init__(): incompatible constructor arguments. The following argument types are supported:
365  1. m.class_.BogusImplicitConversion(arg0: m.class_.BogusImplicitConversion)
366 
367  Invoked with: 0
368  """
369  )
370 
371 
373  with pytest.raises(TypeError) as exc_info:
374  m.test_error_after_conversions("hello")
375  assert str(exc_info.value).startswith(
376  "Unable to convert function return value to a Python type!"
377  )
378 
379 
381  if hasattr(m, "Aligned"):
382  p = m.Aligned().ptr()
383  assert p % 1024 == 0
384 
385 
386 # https://foss.heptapod.net/pypy/pypy/-/issues/2742
387 @pytest.mark.xfail("env.PYPY")
389  with pytest.raises(TypeError) as exc_info:
390 
391  class PyFinalChild(m.IsFinal):
392  pass
393 
394  assert str(exc_info.value).endswith("is not an acceptable base type")
395 
396 
397 # https://foss.heptapod.net/pypy/pypy/-/issues/2742
398 @pytest.mark.xfail("env.PYPY")
400  with pytest.raises(TypeError) as exc_info:
401 
402  class PyNonFinalFinalChild(m.IsNonFinalFinal):
403  pass
404 
405  assert str(exc_info.value).endswith("is not an acceptable base type")
406 
407 
408 # https://github.com/pybind/pybind11/issues/1878
410  with pytest.raises(RuntimeError):
411  m.PyPrintDestructor().throw_something()
412 
413 
414 # https://github.com/pybind/pybind11/issues/1568
416  n = 100
417  instances = [m.SamePointer() for _ in range(n)]
418  for i in range(n):
419  # We need to reuse the same allocated memory for with a different type,
420  # to ensure the bug in `deregister_instance_impl` is detected. Otherwise
421  # `Py_TYPE(self) == Py_TYPE(it->second)` will still succeed, even though
422  # the `instance` is already deleted.
423  instances[i] = m.Empty()
424  # No assert: if this does not trigger the error
425  # pybind11_fail("pybind11_object_dealloc(): Tried to deallocate unregistered instance!");
426  # and just completes without crashing, we're good.
427 
428 
429 # https://github.com/pybind/pybind11/issues/1624
431  assert issubclass(m.DerivedWithNested, m.BaseWithNested)
432  assert m.BaseWithNested.Nested != m.DerivedWithNested.Nested
433  assert m.BaseWithNested.Nested.get_name() == "BaseWithNested::Nested"
434  assert m.DerivedWithNested.Nested.get_name() == "DerivedWithNested::Nested"
435 
436 
438  import types
439 
440  module_scope = types.ModuleType("module_scope")
441  with pytest.raises(RuntimeError) as exc_info:
442  m.register_duplicate_class_name(module_scope)
443  expected = (
444  'generic_type: cannot initialize type "Duplicate": '
445  "an object with that name is already defined"
446  )
447  assert str(exc_info.value) == expected
448  with pytest.raises(RuntimeError) as exc_info:
449  m.register_duplicate_class_type(module_scope)
450  expected = 'generic_type: type "YetAnotherDuplicate" is already registered!'
451  assert str(exc_info.value) == expected
452 
453  class ClassScope:
454  pass
455 
456  with pytest.raises(RuntimeError) as exc_info:
457  m.register_duplicate_nested_class_name(ClassScope)
458  expected = (
459  'generic_type: cannot initialize type "DuplicateNested": '
460  "an object with that name is already defined"
461  )
462  assert str(exc_info.value) == expected
463  with pytest.raises(RuntimeError) as exc_info:
464  m.register_duplicate_nested_class_type(ClassScope)
465  expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
466  assert str(exc_info.value) == expected
test_class.test_exception_rvalue_abort
def test_exception_rvalue_abort()
Definition: test_class.py:409
test_class.test_aligned
def test_aligned()
Definition: test_class.py:380
test_class.test_repr
def test_repr()
Definition: test_class.py:10
test_class.test_inheritance
def test_inheritance(msg)
Definition: test_class.py:138
test_class.test_qualname
def test_qualname(doc)
Definition: test_class.py:98
hasattr
bool hasattr(handle obj, handle name)
Definition: pytypes.h:405
type
Definition: pytypes.h:915
test_class.test_final
def test_final()
Definition: test_class.py:388
test_class.test_override_static
def test_override_static()
Definition: test_class.py:236
ConstructorStats::get
static ConstructorStats & get(std::type_index type)
Definition: constructor_stats.h:154
test_class.test_non_final_final
def test_non_final_final()
Definition: test_class.py:399
doc
Annotation for documentation.
Definition: attr.h:33
test_class.test_as_type_py
def test_as_type_py()
Definition: test_class.py:61
dict
Definition: pytypes.h:1299
isinstance
bool isinstance(handle obj)
Definition: pytypes.h:386
test_class.test_type_of_classic
def test_type_of_classic()
Definition: test_class.py:50
test_class.test_register_duplicate_class
def test_register_duplicate_class()
Definition: test_class.py:437
test_class.test_type_of_py_nodelete
def test_type_of_py_nodelete()
Definition: test_class.py:56
test_class.test_error_after_conversions
def test_error_after_conversions()
Definition: test_class.py:372
test_class.test_instance
def test_instance(msg)
Definition: test_class.py:16
test_class.test_isinstance
def test_isinstance()
Definition: test_class.py:210
str
Definition: pytypes.h:946
test_class.test_type_of_py
def test_type_of_py()
Definition: test_class.py:44
test_class.test_class_refcount
def test_class_refcount()
Definition: test_class.py:337
test_class.test_base_and_derived_nested_scope
def test_base_and_derived_nested_scope()
Definition: test_class.py:430
test_class.test_docstrings
def test_docstrings(doc)
Definition: test_class.py:71
test_class.test_mismatched_holder
def test_mismatched_holder()
Definition: test_class.py:216
tuple
Definition: pytypes.h:1276
test_class.test_inheritance_init
def test_inheritance_init(msg)
Definition: test_class.py:173
test_class.test_automatic_upcasting
def test_automatic_upcasting()
Definition: test_class.py:196
test_class.test_bind_protected_functions
def test_bind_protected_functions()
Definition: test_class.py:304
test_class.test_brace_initialization
def test_brace_initialization()
Definition: test_class.py:323
test_class.test_implicit_conversion_life_support
def test_implicit_conversion_life_support()
Definition: test_class.py:247
test_class.test_multiple_instances_with_same_pointer
def test_multiple_instances_with_same_pointer(capture)
Definition: test_class.py:415
test_class.test_operator_new_delete
def test_operator_new_delete(capture)
Definition: test_class.py:255
test_class.test_reentrant_implicit_conversion_failure
def test_reentrant_implicit_conversion_failure(msg)
Definition: test_class.py:357
test_class.test_type
def test_type()
Definition: test_class.py:29
setup.msg
msg
Definition: setup.py:47
repr
str repr(handle h)
Definition: pytypes.h:1584