cppyabm  1.0.17
An agent-based library to integrate C++ and Python
test_methods_and_attributes.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 methods_and_attributes as m
7 from pybind11_tests import ConstructorStats
8 
9 
11  instance1 = m.ExampleMandA()
12  instance2 = m.ExampleMandA(32)
13 
14  instance1.add1(instance2)
15  instance1.add2(instance2)
16  instance1.add3(instance2)
17  instance1.add4(instance2)
18  instance1.add5(instance2)
19  instance1.add6(32)
20  instance1.add7(32)
21  instance1.add8(32)
22  instance1.add9(32)
23  instance1.add10(32)
24 
25  assert str(instance1) == "ExampleMandA[value=320]"
26  assert str(instance2) == "ExampleMandA[value=32]"
27  assert str(instance1.self1()) == "ExampleMandA[value=320]"
28  assert str(instance1.self2()) == "ExampleMandA[value=320]"
29  assert str(instance1.self3()) == "ExampleMandA[value=320]"
30  assert str(instance1.self4()) == "ExampleMandA[value=320]"
31  assert str(instance1.self5()) == "ExampleMandA[value=320]"
32 
33  assert instance1.internal1() == 320
34  assert instance1.internal2() == 320
35  assert instance1.internal3() == 320
36  assert instance1.internal4() == 320
37  assert instance1.internal5() == 320
38 
39  assert instance1.overloaded() == "()"
40  assert instance1.overloaded(0) == "(int)"
41  assert instance1.overloaded(1, 1.0) == "(int, float)"
42  assert instance1.overloaded(2.0, 2) == "(float, int)"
43  assert instance1.overloaded(3, 3) == "(int, int)"
44  assert instance1.overloaded(4.0, 4.0) == "(float, float)"
45  assert instance1.overloaded_const(-3) == "(int) const"
46  assert instance1.overloaded_const(5, 5.0) == "(int, float) const"
47  assert instance1.overloaded_const(6.0, 6) == "(float, int) const"
48  assert instance1.overloaded_const(7, 7) == "(int, int) const"
49  assert instance1.overloaded_const(8.0, 8.0) == "(float, float) const"
50  assert instance1.overloaded_float(1, 1) == "(float, float)"
51  assert instance1.overloaded_float(1, 1.0) == "(float, float)"
52  assert instance1.overloaded_float(1.0, 1) == "(float, float)"
53  assert instance1.overloaded_float(1.0, 1.0) == "(float, float)"
54 
55  assert instance1.value == 320
56  instance1.value = 100
57  assert str(instance1) == "ExampleMandA[value=100]"
58 
59  cstats = ConstructorStats.get(m.ExampleMandA)
60  assert cstats.alive() == 2
61  del instance1, instance2
62  assert cstats.alive() == 0
63  assert cstats.values() == ["32"]
64  assert cstats.default_constructions == 1
65  assert cstats.copy_constructions == 2
66  assert cstats.move_constructions >= 2
67  assert cstats.copy_assignments == 0
68  assert cstats.move_assignments == 0
69 
70 
72  """Issue #443: calling copied methods fails in Python 3"""
73 
74  m.ExampleMandA.add2c = m.ExampleMandA.add2
75  m.ExampleMandA.add2d = m.ExampleMandA.add2b
76  a = m.ExampleMandA(123)
77  assert a.value == 123
78  a.add2(m.ExampleMandA(-100))
79  assert a.value == 23
80  a.add2b(m.ExampleMandA(20))
81  assert a.value == 43
82  a.add2c(m.ExampleMandA(6))
83  assert a.value == 49
84  a.add2d(m.ExampleMandA(-7))
85  assert a.value == 42
86 
87 
89  instance = m.TestProperties()
90 
91  assert instance.def_readonly == 1
92  with pytest.raises(AttributeError):
93  instance.def_readonly = 2
94 
95  instance.def_readwrite = 2
96  assert instance.def_readwrite == 2
97 
98  assert instance.def_property_readonly == 2
99  with pytest.raises(AttributeError):
100  instance.def_property_readonly = 3
101 
102  instance.def_property = 3
103  assert instance.def_property == 3
104 
105  with pytest.raises(AttributeError) as excinfo:
106  dummy = instance.def_property_writeonly # noqa: F841 unused var
107  assert "unreadable attribute" in str(excinfo.value)
108 
109  instance.def_property_writeonly = 4
110  assert instance.def_property_readonly == 4
111 
112  with pytest.raises(AttributeError) as excinfo:
113  dummy = instance.def_property_impossible # noqa: F841 unused var
114  assert "unreadable attribute" in str(excinfo.value)
115 
116  with pytest.raises(AttributeError) as excinfo:
117  instance.def_property_impossible = 5
118  assert "can't set attribute" in str(excinfo.value)
119 
120 
122  assert m.TestProperties.def_readonly_static == 1
123  with pytest.raises(AttributeError) as excinfo:
124  m.TestProperties.def_readonly_static = 2
125  assert "can't set attribute" in str(excinfo.value)
126 
127  m.TestProperties.def_readwrite_static = 2
128  assert m.TestProperties.def_readwrite_static == 2
129 
130  with pytest.raises(AttributeError) as excinfo:
131  dummy = m.TestProperties.def_writeonly_static # noqa: F841 unused var
132  assert "unreadable attribute" in str(excinfo.value)
133 
134  m.TestProperties.def_writeonly_static = 3
135  assert m.TestProperties.def_readonly_static == 3
136 
137  assert m.TestProperties.def_property_readonly_static == 3
138  with pytest.raises(AttributeError) as excinfo:
139  m.TestProperties.def_property_readonly_static = 99
140  assert "can't set attribute" in str(excinfo.value)
141 
142  m.TestProperties.def_property_static = 4
143  assert m.TestProperties.def_property_static == 4
144 
145  with pytest.raises(AttributeError) as excinfo:
146  dummy = m.TestProperties.def_property_writeonly_static
147  assert "unreadable attribute" in str(excinfo.value)
148 
149  m.TestProperties.def_property_writeonly_static = 5
150  assert m.TestProperties.def_property_static == 5
151 
152  # Static property read and write via instance
153  instance = m.TestProperties()
154 
155  m.TestProperties.def_readwrite_static = 0
156  assert m.TestProperties.def_readwrite_static == 0
157  assert instance.def_readwrite_static == 0
158 
159  instance.def_readwrite_static = 2
160  assert m.TestProperties.def_readwrite_static == 2
161  assert instance.def_readwrite_static == 2
162 
163  with pytest.raises(AttributeError) as excinfo:
164  dummy = instance.def_property_writeonly_static # noqa: F841 unused var
165  assert "unreadable attribute" in str(excinfo.value)
166 
167  instance.def_property_writeonly_static = 4
168  assert instance.def_property_static == 4
169 
170  # It should be possible to override properties in derived classes
171  assert m.TestPropertiesOverride().def_readonly == 99
172  assert m.TestPropertiesOverride.def_readonly_static == 99
173 
174  # Only static attributes can be deleted
175  del m.TestPropertiesOverride.def_readonly_static
176  assert (
177  hasattr(m.TestPropertiesOverride, "def_readonly_static")
178  and m.TestPropertiesOverride.def_readonly_static
179  is m.TestProperties.def_readonly_static
180  )
181  assert "def_readonly_static" not in m.TestPropertiesOverride.__dict__
182  properties_override = m.TestPropertiesOverride()
183  with pytest.raises(AttributeError) as excinfo:
184  del properties_override.def_readonly
185  assert "can't delete attribute" in str(excinfo.value)
186 
187 
189  """Static property getter and setters expect the type object as the their only argument"""
190 
191  instance = m.TestProperties()
192  assert m.TestProperties.static_cls is m.TestProperties
193  assert instance.static_cls is m.TestProperties
194 
195  def check_self(self):
196  assert self is m.TestProperties
197 
198  m.TestProperties.static_cls = check_self
199  instance.static_cls = check_self
200 
201 
203  """Overriding pybind11's default metaclass changes the behavior of `static_property`"""
204 
205  assert type(m.ExampleMandA).__name__ == "pybind11_type"
206  assert type(m.MetaclassOverride).__name__ == "type"
207 
208  assert m.MetaclassOverride.readonly == 1
209  assert (
210  type(m.MetaclassOverride.__dict__["readonly"]).__name__
211  == "pybind11_static_property"
212  )
213 
214  # Regular `type` replaces the property instead of calling `__set__()`
215  m.MetaclassOverride.readonly = 2
216  assert m.MetaclassOverride.readonly == 2
217  assert isinstance(m.MetaclassOverride.__dict__["readonly"], int)
218 
219 
221  from pybind11_tests import debug_enabled
222 
223  with pytest.raises(RuntimeError) as excinfo:
224  m.ExampleMandA.add_mixed_overloads1()
225  assert str(
226  excinfo.value
227  ) == "overloading a method with both static and instance methods is not supported; " + (
228  "compile in debug mode for more details"
229  if not debug_enabled
230  else "error while attempting to bind static method ExampleMandA.overload_mixed1"
231  "(arg0: float) -> str"
232  )
233 
234  with pytest.raises(RuntimeError) as excinfo:
235  m.ExampleMandA.add_mixed_overloads2()
236  assert str(
237  excinfo.value
238  ) == "overloading a method with both static and instance methods is not supported; " + (
239  "compile in debug mode for more details"
240  if not debug_enabled
241  else "error while attempting to bind instance method ExampleMandA.overload_mixed2"
242  "(self: pybind11_tests.methods_and_attributes.ExampleMandA, arg0: int, arg1: int)"
243  " -> str"
244  )
245 
246 
247 @pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
249  if not access.startswith("static"):
250  obj = m.TestPropRVP()
251  else:
252  obj = m.TestPropRVP
253 
254  ref = getattr(obj, access + "_ref")
255  assert ref.value == 1
256  ref.value = 2
257  assert getattr(obj, access + "_ref").value == 2
258  ref.value = 1 # restore original value for static properties
259 
260  copy = getattr(obj, access + "_copy")
261  assert copy.value == 1
262  copy.value = 2
263  assert getattr(obj, access + "_copy").value == 1
264 
265  copy = getattr(obj, access + "_func")
266  assert copy.value == 1
267  copy.value = 2
268  assert getattr(obj, access + "_func").value == 1
269 
270 
272  """When returning an rvalue, the return value policy is automatically changed from
273  `reference(_internal)` to `move`. The following would not work otherwise."""
274 
275  instance = m.TestPropRVP()
276  o = instance.rvalue
277  assert o.value == 1
278 
279  os = m.TestPropRVP.static_rvalue
280  assert os.value == 1
281 
282 
283 # https://foss.heptapod.net/pypy/pypy/-/issues/2447
284 @pytest.mark.xfail("env.PYPY")
286  instance = m.DynamicClass()
287  assert not hasattr(instance, "foo")
288  assert "foo" not in dir(instance)
289 
290  # Dynamically add attribute
291  instance.foo = 42
292  assert hasattr(instance, "foo")
293  assert instance.foo == 42
294  assert "foo" in dir(instance)
295 
296  # __dict__ should be accessible and replaceable
297  assert "foo" in instance.__dict__
298  instance.__dict__ = {"bar": True}
299  assert not hasattr(instance, "foo")
300  assert hasattr(instance, "bar")
301 
302  with pytest.raises(TypeError) as excinfo:
303  instance.__dict__ = []
304  assert str(excinfo.value) == "__dict__ must be set to a dictionary, not a 'list'"
305 
306  cstats = ConstructorStats.get(m.DynamicClass)
307  assert cstats.alive() == 1
308  del instance
309  assert cstats.alive() == 0
310 
311  # Derived classes should work as well
312  class PythonDerivedDynamicClass(m.DynamicClass):
313  pass
314 
315  for cls in m.CppDerivedDynamicClass, PythonDerivedDynamicClass:
316  derived = cls()
317  derived.foobar = 100
318  assert derived.foobar == 100
319 
320  assert cstats.alive() == 1
321  del derived
322  assert cstats.alive() == 0
323 
324 
325 # https://foss.heptapod.net/pypy/pypy/-/issues/2447
326 @pytest.mark.xfail("env.PYPY")
328  # One object references itself
329  instance = m.DynamicClass()
330  instance.circular_reference = instance
331 
332  cstats = ConstructorStats.get(m.DynamicClass)
333  assert cstats.alive() == 1
334  del instance
335  assert cstats.alive() == 0
336 
337  # Two object reference each other
338  i1 = m.DynamicClass()
339  i2 = m.DynamicClass()
340  i1.cycle = i2
341  i2.cycle = i1
342 
343  assert cstats.alive() == 2
344  del i1, i2
345  assert cstats.alive() == 0
346 
347 
349  from pybind11_tests import debug_enabled
350 
351  with pytest.raises(RuntimeError) as excinfo:
352  m.bad_arg_def_named()
353  assert msg(excinfo.value) == (
354  "arg(): could not convert default argument 'a: UnregisteredType' in function "
355  "'should_fail' into a Python object (type not registered yet?)"
356  if debug_enabled
357  else "arg(): could not convert default argument into a Python object (type not registered "
358  "yet?). Compile in debug mode for more information."
359  )
360 
361  with pytest.raises(RuntimeError) as excinfo:
362  m.bad_arg_def_unnamed()
363  assert msg(excinfo.value) == (
364  "arg(): could not convert default argument 'UnregisteredType' in function "
365  "'should_fail' into a Python object (type not registered yet?)"
366  if debug_enabled
367  else "arg(): could not convert default argument into a Python object (type not registered "
368  "yet?). Compile in debug mode for more information."
369  )
370 
371 
373  a = m.NoneTester()
374  assert m.no_none1(a) == 42
375  assert m.no_none2(a) == 42
376  assert m.no_none3(a) == 42
377  assert m.no_none4(a) == 42
378  assert m.no_none5(a) == 42
379  assert m.ok_none1(a) == 42
380  assert m.ok_none2(a) == 42
381  assert m.ok_none3(a) == 42
382  assert m.ok_none4(a) == 42
383  assert m.ok_none5(a) == 42
384 
385  with pytest.raises(TypeError) as excinfo:
386  m.no_none1(None)
387  assert "incompatible function arguments" in str(excinfo.value)
388  with pytest.raises(TypeError) as excinfo:
389  m.no_none2(None)
390  assert "incompatible function arguments" in str(excinfo.value)
391  with pytest.raises(TypeError) as excinfo:
392  m.no_none3(None)
393  assert "incompatible function arguments" in str(excinfo.value)
394  with pytest.raises(TypeError) as excinfo:
395  m.no_none4(None)
396  assert "incompatible function arguments" in str(excinfo.value)
397  with pytest.raises(TypeError) as excinfo:
398  m.no_none5(None)
399  assert "incompatible function arguments" in str(excinfo.value)
400 
401  # The first one still raises because you can't pass None as a lvalue reference arg:
402  with pytest.raises(TypeError) as excinfo:
403  assert m.ok_none1(None) == -1
404  assert (
405  msg(excinfo.value)
406  == """
407  ok_none1(): incompatible function arguments. The following argument types are supported:
408  1. (arg0: m.methods_and_attributes.NoneTester) -> int
409 
410  Invoked with: None
411  """
412  )
413 
414  # The rest take the argument as pointer or holder, and accept None:
415  assert m.ok_none2(None) == -1
416  assert m.ok_none3(None) == -1
417  assert m.ok_none4(None) == -1
418  assert m.ok_none5(None) == -1
419 
420  with pytest.raises(TypeError) as excinfo:
421  m.no_none_kwarg(None)
422  assert "incompatible function arguments" in str(excinfo.value)
423  with pytest.raises(TypeError) as excinfo:
424  m.no_none_kwarg(a=None)
425  assert "incompatible function arguments" in str(excinfo.value)
426  with pytest.raises(TypeError) as excinfo:
427  m.no_none_kwarg_kw_only(None)
428  assert "incompatible function arguments" in str(excinfo.value)
429  with pytest.raises(TypeError) as excinfo:
430  m.no_none_kwarg_kw_only(a=None)
431  assert "incompatible function arguments" in str(excinfo.value)
432 
433 
434 def test_str_issue(msg):
435  """#283: __str__ called on uninitialized instance when constructor arguments invalid"""
436 
437  assert str(m.StrIssue(3)) == "StrIssue[3]"
438 
439  with pytest.raises(TypeError) as excinfo:
440  str(m.StrIssue("no", "such", "constructor"))
441  assert (
442  msg(excinfo.value)
443  == """
444  __init__(): incompatible constructor arguments. The following argument types are supported:
445  1. m.methods_and_attributes.StrIssue(arg0: int)
446  2. m.methods_and_attributes.StrIssue()
447 
448  Invoked with: 'no', 'such', 'constructor'
449  """
450  )
451 
452 
454  a = m.RegisteredDerived()
455  a.do_nothing()
456  assert a.rw_value == 42
457  assert a.ro_value == 1.25
458  a.rw_value += 5
459  assert a.sum() == 48.25
460  a.increase_value()
461  assert a.rw_value == 48
462  assert a.ro_value == 1.5
463  assert a.sum() == 49.5
464  assert a.rw_value_prop == 48
465  a.rw_value_prop += 1
466  assert a.rw_value_prop == 49
467  a.increase_value()
468  assert a.ro_value_prop == 1.75
469 
470 
472  """Tests that explicit lvalue ref-qualified methods can be called just like their
473  non ref-qualified counterparts."""
474 
475  r = m.RefQualified()
476  assert r.value == 0
477  r.refQualified(17)
478  assert r.value == 17
479  assert r.constRefQualified(23) == 40
480 
481 
483  "Check to see if the normal overload order (first defined) and prepend overload order works"
484  assert m.overload_order("string") == 1
485  assert m.overload_order(0) == 4
486 
487  # Different for Python 2 vs. 3
488  uni_name = type(u"").__name__
489 
490  assert "1. overload_order(arg0: int) -> int" in m.overload_order.__doc__
491  assert (
492  "2. overload_order(arg0: {}) -> int".format(uni_name)
493  in m.overload_order.__doc__
494  )
495  assert (
496  "3. overload_order(arg0: {}) -> int".format(uni_name)
497  in m.overload_order.__doc__
498  )
499  assert "4. overload_order(arg0: int) -> int" in m.overload_order.__doc__
500 
501  with pytest.raises(TypeError) as err:
502  m.overload_order(1.1)
503 
504  assert "1. (arg0: int) -> int" in str(err.value)
505  assert "2. (arg0: {}) -> int".format(uni_name) in str(err.value)
506  assert "3. (arg0: {}) -> int".format(uni_name) in str(err.value)
507  assert "4. (arg0: int) -> int" in str(err.value)
test_methods_and_attributes.test_ref_qualified
def test_ref_qualified()
Definition: test_methods_and_attributes.py:471
hasattr
bool hasattr(handle obj, handle name)
Definition: pytypes.h:405
test_methods_and_attributes.test_copy_method
def test_copy_method()
Definition: test_methods_and_attributes.py:71
type
Definition: pytypes.h:915
getattr
object getattr(handle obj, handle name)
Definition: pytypes.h:421
test_methods_and_attributes.test_properties
def test_properties()
Definition: test_methods_and_attributes.py:88
test_methods_and_attributes.test_no_mixed_overloads
def test_no_mixed_overloads()
Definition: test_methods_and_attributes.py:220
ConstructorStats::get
static ConstructorStats & get(std::type_index type)
Definition: constructor_stats.h:154
test_methods_and_attributes.test_unregistered_base_implementations
def test_unregistered_base_implementations()
Definition: test_methods_and_attributes.py:453
test_methods_and_attributes.test_bad_arg_default
def test_bad_arg_default(msg)
Definition: test_methods_and_attributes.py:348
isinstance
bool isinstance(handle obj)
Definition: pytypes.h:386
test_methods_and_attributes.test_static_properties
def test_static_properties()
Definition: test_methods_and_attributes.py:121
test_methods_and_attributes.test_cyclic_gc
def test_cyclic_gc()
Definition: test_methods_and_attributes.py:327
test_methods_and_attributes.test_property_return_value_policies
def test_property_return_value_policies(access)
Definition: test_methods_and_attributes.py:248
str
Definition: pytypes.h:946
test_methods_and_attributes.test_static_cls
def test_static_cls()
Definition: test_methods_and_attributes.py:188
test_methods_and_attributes.test_property_rvalue_policy
def test_property_rvalue_policy()
Definition: test_methods_and_attributes.py:271
test_methods_and_attributes.test_metaclass_override
def test_metaclass_override()
Definition: test_methods_and_attributes.py:202
test_methods_and_attributes.test_overload_ordering
def test_overload_ordering()
Definition: test_methods_and_attributes.py:482
test_methods_and_attributes.test_str_issue
def test_str_issue(msg)
Definition: test_methods_and_attributes.py:434
test_methods_and_attributes.test_methods_and_attributes
def test_methods_and_attributes()
Definition: test_methods_and_attributes.py:10
test_methods_and_attributes.test_accepts_none
def test_accepts_none(msg)
Definition: test_methods_and_attributes.py:372
test_methods_and_attributes.test_dynamic_attributes
def test_dynamic_attributes()
Definition: test_methods_and_attributes.py:285
setup.msg
msg
Definition: setup.py:47