mirror of
https://github.com/RYDE-WORK/pybind11.git
synced 2026-01-20 21:53:42 +08:00
* Disable implicit conversion from `0` to `pybind11::handle`. * Reverse or-ed condition in an attempt to resolve GCC 8.3.0 errors (i386/debian:buster). * Trying the simpler `std::is_same<T, PyObject *>` * Add implicit_conversion_from_pytorch_THPObjectPtr_to_handle test. * Accommodate types with implicit conversions to `PyObject *`, other than `handle` & `handle` subclasses, or integral types. * Fix copy-paste mishap (picked wrong name). * Revamp SFINAE construct to actually fix the pytorch issue (already validated against pytorch proper). The first version of the reduced pytorch code was critically missing the move ctor. The first version of the accompanying test was meaningless. Note: It turns out the `!std::is_arithmetic<T>` condition is not needed: `int` is not in general implicitly convertible to `PyObject *`, only the literal `0` is. * Use `NOLINT(performance-noexcept-move-constructor)` for reduced code from the wild (rather than changing the code). * Use any_of, all_of, negation. It turns out to clang-format nicer. * Clean up comments for changed code. * Reduce pytorch situation further, add test for operator ... const. * Use `none_of` as suggested by @skylion007 * Add `pure_compile_tests_for_handle_from_PyObject_pointers()` * Fix inconsequential oversight (retested). * Factor our `is_pyobj_ptr_or_nullptr_t` to make the SFINAE conditions more readable. * Remove stray line (oversight). * Make the `pure_compile_tests_for_handle_from_PyObject_pointers()` "rhs-const-complete", too. * Remove the temporary PYBIND11_UNDO_PR4008 `#ifdef`.
742 lines
20 KiB
Python
742 lines
20 KiB
Python
import contextlib
|
|
import sys
|
|
import types
|
|
|
|
import pytest
|
|
|
|
import env
|
|
from pybind11_tests import detailed_error_messages_enabled
|
|
from pybind11_tests import pytypes as m
|
|
|
|
|
|
def test_handle_from_move_only_type_with_operator_PyObject(): # noqa: N802
|
|
assert m.handle_from_move_only_type_with_operator_PyObject_ncnst()
|
|
assert m.handle_from_move_only_type_with_operator_PyObject_const()
|
|
|
|
|
|
def test_bool(doc):
|
|
assert doc(m.get_bool) == "get_bool() -> bool"
|
|
|
|
|
|
def test_int(doc):
|
|
assert doc(m.get_int) == "get_int() -> int"
|
|
|
|
|
|
def test_iterator(doc):
|
|
assert doc(m.get_iterator) == "get_iterator() -> Iterator"
|
|
|
|
|
|
def test_iterable(doc):
|
|
assert doc(m.get_iterable) == "get_iterable() -> Iterable"
|
|
|
|
|
|
def test_float(doc):
|
|
assert doc(m.get_float) == "get_float() -> float"
|
|
|
|
|
|
def test_list(capture, doc):
|
|
assert m.list_no_args() == []
|
|
assert m.list_ssize_t() == []
|
|
assert m.list_size_t() == []
|
|
lins = [1, 2]
|
|
m.list_insert_ssize_t(lins)
|
|
assert lins == [1, 83, 2]
|
|
m.list_insert_size_t(lins)
|
|
assert lins == [1, 83, 2, 57]
|
|
|
|
with capture:
|
|
lst = m.get_list()
|
|
assert lst == ["inserted-0", "overwritten", "inserted-2"]
|
|
|
|
lst.append("value2")
|
|
m.print_list(lst)
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
Entry at position 0: value
|
|
list item 0: inserted-0
|
|
list item 1: overwritten
|
|
list item 2: inserted-2
|
|
list item 3: value2
|
|
"""
|
|
)
|
|
|
|
assert doc(m.get_list) == "get_list() -> list"
|
|
assert doc(m.print_list) == "print_list(arg0: list) -> None"
|
|
|
|
|
|
def test_none(capture, doc):
|
|
assert doc(m.get_none) == "get_none() -> None"
|
|
assert doc(m.print_none) == "print_none(arg0: None) -> None"
|
|
|
|
|
|
def test_set(capture, doc):
|
|
s = m.get_set()
|
|
assert isinstance(s, set)
|
|
assert s == {"key1", "key2", "key3"}
|
|
|
|
s.add("key4")
|
|
with capture:
|
|
m.print_anyset(s)
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
key: key1
|
|
key: key2
|
|
key: key3
|
|
key: key4
|
|
"""
|
|
)
|
|
|
|
m.set_add(s, "key5")
|
|
assert m.anyset_size(s) == 5
|
|
|
|
m.set_clear(s)
|
|
assert m.anyset_empty(s)
|
|
|
|
assert not m.anyset_contains(set(), 42)
|
|
assert m.anyset_contains({42}, 42)
|
|
assert m.anyset_contains({"foo"}, "foo")
|
|
|
|
assert doc(m.get_set) == "get_set() -> set"
|
|
assert doc(m.print_anyset) == "print_anyset(arg0: anyset) -> None"
|
|
|
|
|
|
def test_frozenset(capture, doc):
|
|
s = m.get_frozenset()
|
|
assert isinstance(s, frozenset)
|
|
assert s == frozenset({"key1", "key2", "key3"})
|
|
|
|
with capture:
|
|
m.print_anyset(s)
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
key: key1
|
|
key: key2
|
|
key: key3
|
|
"""
|
|
)
|
|
assert m.anyset_size(s) == 3
|
|
assert not m.anyset_empty(s)
|
|
|
|
assert not m.anyset_contains(frozenset(), 42)
|
|
assert m.anyset_contains(frozenset({42}), 42)
|
|
assert m.anyset_contains(frozenset({"foo"}), "foo")
|
|
|
|
assert doc(m.get_frozenset) == "get_frozenset() -> frozenset"
|
|
|
|
|
|
def test_dict(capture, doc):
|
|
d = m.get_dict()
|
|
assert d == {"key": "value"}
|
|
|
|
with capture:
|
|
d["key2"] = "value2"
|
|
m.print_dict(d)
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
key: key, value=value
|
|
key: key2, value=value2
|
|
"""
|
|
)
|
|
|
|
assert not m.dict_contains({}, 42)
|
|
assert m.dict_contains({42: None}, 42)
|
|
assert m.dict_contains({"foo": None}, "foo")
|
|
|
|
assert doc(m.get_dict) == "get_dict() -> dict"
|
|
assert doc(m.print_dict) == "print_dict(arg0: dict) -> None"
|
|
|
|
assert m.dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}
|
|
|
|
|
|
def test_tuple():
|
|
assert m.tuple_no_args() == ()
|
|
assert m.tuple_ssize_t() == ()
|
|
assert m.tuple_size_t() == ()
|
|
assert m.get_tuple() == (42, None, "spam")
|
|
|
|
|
|
def test_simple_namespace():
|
|
ns = m.get_simple_namespace()
|
|
assert ns.attr == 42
|
|
assert ns.x == "foo"
|
|
assert ns.right == 2
|
|
assert not hasattr(ns, "wrong")
|
|
|
|
|
|
def test_str(doc):
|
|
assert m.str_from_char_ssize_t().encode().decode() == "red"
|
|
assert m.str_from_char_size_t().encode().decode() == "blue"
|
|
assert m.str_from_string().encode().decode() == "baz"
|
|
assert m.str_from_bytes().encode().decode() == "boo"
|
|
|
|
assert doc(m.str_from_bytes) == "str_from_bytes() -> str"
|
|
|
|
class A:
|
|
def __str__(self):
|
|
return "this is a str"
|
|
|
|
def __repr__(self):
|
|
return "this is a repr"
|
|
|
|
assert m.str_from_object(A()) == "this is a str"
|
|
assert m.repr_from_object(A()) == "this is a repr"
|
|
assert m.str_from_handle(A()) == "this is a str"
|
|
|
|
s1, s2 = m.str_format()
|
|
assert s1 == "1 + 2 = 3"
|
|
assert s1 == s2
|
|
|
|
malformed_utf8 = b"\x80"
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert m.str_from_object(malformed_utf8) is malformed_utf8
|
|
else:
|
|
assert m.str_from_object(malformed_utf8) == "b'\\x80'"
|
|
assert m.str_from_handle(malformed_utf8) == "b'\\x80'"
|
|
|
|
assert m.str_from_string_from_str("this is a str") == "this is a str"
|
|
ucs_surrogates_str = "\udcc3"
|
|
with pytest.raises(UnicodeEncodeError):
|
|
m.str_from_string_from_str(ucs_surrogates_str)
|
|
|
|
|
|
def test_bytes(doc):
|
|
assert m.bytes_from_char_ssize_t().decode() == "green"
|
|
assert m.bytes_from_char_size_t().decode() == "purple"
|
|
assert m.bytes_from_string().decode() == "foo"
|
|
assert m.bytes_from_str().decode() == "bar"
|
|
|
|
assert doc(m.bytes_from_str) == "bytes_from_str() -> bytes"
|
|
|
|
|
|
def test_bytearray(doc):
|
|
assert m.bytearray_from_char_ssize_t().decode() == "$%"
|
|
assert m.bytearray_from_char_size_t().decode() == "@$!"
|
|
assert m.bytearray_from_string().decode() == "foo"
|
|
assert m.bytearray_size() == len("foo")
|
|
|
|
|
|
def test_capsule(capture):
|
|
pytest.gc_collect()
|
|
with capture:
|
|
a = m.return_capsule_with_destructor()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule
|
|
destructing capsule
|
|
"""
|
|
)
|
|
|
|
with capture:
|
|
a = m.return_renamed_capsule_with_destructor()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule
|
|
renaming capsule
|
|
destructing capsule
|
|
"""
|
|
)
|
|
|
|
with capture:
|
|
a = m.return_capsule_with_destructor_2()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule
|
|
destructing capsule: 1234
|
|
"""
|
|
)
|
|
|
|
with capture:
|
|
a = m.return_renamed_capsule_with_destructor_2()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule
|
|
renaming capsule
|
|
destructing capsule: 1234
|
|
"""
|
|
)
|
|
|
|
with capture:
|
|
a = m.return_capsule_with_name_and_destructor()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
created capsule (1234, 'pointer type description')
|
|
destructing capsule (1234, 'pointer type description')
|
|
"""
|
|
)
|
|
|
|
|
|
def test_accessors():
|
|
class SubTestObject:
|
|
attr_obj = 1
|
|
attr_char = 2
|
|
|
|
class TestObject:
|
|
basic_attr = 1
|
|
begin_end = [1, 2, 3]
|
|
d = {"operator[object]": 1, "operator[char *]": 2}
|
|
sub = SubTestObject()
|
|
|
|
def func(self, x, *args):
|
|
return self.basic_attr + x + sum(args)
|
|
|
|
d = m.accessor_api(TestObject())
|
|
assert d["basic_attr"] == 1
|
|
assert d["begin_end"] == [1, 2, 3]
|
|
assert d["operator[object]"] == 1
|
|
assert d["operator[char *]"] == 2
|
|
assert d["attr(object)"] == 1
|
|
assert d["attr(char *)"] == 2
|
|
assert d["missing_attr_ptr"] == "raised"
|
|
assert d["missing_attr_chain"] == "raised"
|
|
assert d["is_none"] is False
|
|
assert d["operator()"] == 2
|
|
assert d["operator*"] == 7
|
|
assert d["implicit_list"] == [1, 2, 3]
|
|
assert all(x in TestObject.__dict__ for x in d["implicit_dict"])
|
|
|
|
assert m.tuple_accessor(tuple()) == (0, 1, 2)
|
|
|
|
d = m.accessor_assignment()
|
|
assert d["get"] == 0
|
|
assert d["deferred_get"] == 0
|
|
assert d["set"] == 1
|
|
assert d["deferred_set"] == 1
|
|
assert d["var"] == 99
|
|
|
|
|
|
def test_accessor_moves():
|
|
inc_refs = m.accessor_moves()
|
|
if inc_refs:
|
|
assert inc_refs == [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
|
|
else:
|
|
pytest.skip("Not defined: PYBIND11_HANDLE_REF_DEBUG")
|
|
|
|
|
|
def test_constructors():
|
|
"""C++ default and converting constructors are equivalent to type calls in Python"""
|
|
types = [bytes, bytearray, str, bool, int, float, tuple, list, dict, set]
|
|
expected = {t.__name__: t() for t in types}
|
|
assert m.default_constructors() == expected
|
|
|
|
data = {
|
|
bytes: b"41", # Currently no supported or working conversions.
|
|
bytearray: bytearray(b"41"),
|
|
str: 42,
|
|
bool: "Not empty",
|
|
int: "42",
|
|
float: "+1e3",
|
|
tuple: range(3),
|
|
list: range(3),
|
|
dict: [("two", 2), ("one", 1), ("three", 3)],
|
|
set: [4, 4, 5, 6, 6, 6],
|
|
frozenset: [4, 4, 5, 6, 6, 6],
|
|
memoryview: b"abc",
|
|
}
|
|
inputs = {k.__name__: v for k, v in data.items()}
|
|
expected = {k.__name__: k(v) for k, v in data.items()}
|
|
|
|
assert m.converting_constructors(inputs) == expected
|
|
assert m.cast_functions(inputs) == expected
|
|
|
|
# Converting constructors and cast functions should just reference rather
|
|
# than copy when no conversion is needed:
|
|
noconv1 = m.converting_constructors(expected)
|
|
for k in noconv1:
|
|
assert noconv1[k] is expected[k]
|
|
|
|
noconv2 = m.cast_functions(expected)
|
|
for k in noconv2:
|
|
assert noconv2[k] is expected[k]
|
|
|
|
|
|
def test_non_converting_constructors():
|
|
non_converting_test_cases = [
|
|
("bytes", range(10)),
|
|
("none", 42),
|
|
("ellipsis", 42),
|
|
("type", 42),
|
|
]
|
|
for t, v in non_converting_test_cases:
|
|
for move in [True, False]:
|
|
with pytest.raises(TypeError) as excinfo:
|
|
m.nonconverting_constructor(t, v, move)
|
|
expected_error = (
|
|
f"Object of type '{type(v).__name__}' is not an instance of '{t}'"
|
|
)
|
|
assert str(excinfo.value) == expected_error
|
|
|
|
|
|
def test_pybind11_str_raw_str():
|
|
# specifically to exercise pybind11::str::raw_str
|
|
cvt = m.convert_to_pybind11_str
|
|
assert cvt("Str") == "Str"
|
|
assert cvt(b"Bytes") == "b'Bytes'"
|
|
assert cvt(None) == "None"
|
|
assert cvt(False) == "False"
|
|
assert cvt(True) == "True"
|
|
assert cvt(42) == "42"
|
|
assert cvt(2**65) == "36893488147419103232"
|
|
assert cvt(-1.50) == "-1.5"
|
|
assert cvt(()) == "()"
|
|
assert cvt((18,)) == "(18,)"
|
|
assert cvt([]) == "[]"
|
|
assert cvt([28]) == "[28]"
|
|
assert cvt({}) == "{}"
|
|
assert cvt({3: 4}) == "{3: 4}"
|
|
assert cvt(set()) == "set()"
|
|
assert cvt({3, 3}) == "{3}"
|
|
|
|
valid_orig = "DZ"
|
|
valid_utf8 = valid_orig.encode("utf-8")
|
|
valid_cvt = cvt(valid_utf8)
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert valid_cvt is valid_utf8
|
|
else:
|
|
assert type(valid_cvt) is str
|
|
assert valid_cvt == "b'\\xc7\\xb1'"
|
|
|
|
malformed_utf8 = b"\x80"
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert cvt(malformed_utf8) is malformed_utf8
|
|
else:
|
|
malformed_cvt = cvt(malformed_utf8)
|
|
assert type(malformed_cvt) is str
|
|
assert malformed_cvt == "b'\\x80'"
|
|
|
|
|
|
def test_implicit_casting():
|
|
"""Tests implicit casting when assigning or appending to dicts and lists."""
|
|
z = m.get_implicit_casting()
|
|
assert z["d"] == {
|
|
"char*_i1": "abc",
|
|
"char*_i2": "abc",
|
|
"char*_e": "abc",
|
|
"char*_p": "abc",
|
|
"str_i1": "str",
|
|
"str_i2": "str1",
|
|
"str_e": "str2",
|
|
"str_p": "str3",
|
|
"int_i1": 42,
|
|
"int_i2": 42,
|
|
"int_e": 43,
|
|
"int_p": 44,
|
|
}
|
|
assert z["l"] == [3, 6, 9, 12, 15]
|
|
|
|
|
|
def test_print(capture):
|
|
with capture:
|
|
m.print_function()
|
|
assert (
|
|
capture
|
|
== """
|
|
Hello, World!
|
|
1 2.0 three True -- multiple args
|
|
*args-and-a-custom-separator
|
|
no new line here -- next print
|
|
flush
|
|
py::print + str.format = this
|
|
"""
|
|
)
|
|
assert capture.stderr == "this goes to stderr"
|
|
|
|
with pytest.raises(RuntimeError) as excinfo:
|
|
m.print_failure()
|
|
assert str(excinfo.value) == "Unable to convert call argument " + (
|
|
"'1' of type 'UnregisteredType' to Python object"
|
|
if detailed_error_messages_enabled
|
|
else "to Python object (#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)"
|
|
)
|
|
|
|
|
|
def test_hash():
|
|
class Hashable:
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def __hash__(self):
|
|
return self.value
|
|
|
|
class Unhashable:
|
|
__hash__ = None
|
|
|
|
assert m.hash_function(Hashable(42)) == 42
|
|
with pytest.raises(TypeError):
|
|
m.hash_function(Unhashable())
|
|
|
|
|
|
def test_number_protocol():
|
|
for a, b in [(1, 1), (3, 5)]:
|
|
li = [
|
|
a == b,
|
|
a != b,
|
|
a < b,
|
|
a <= b,
|
|
a > b,
|
|
a >= b,
|
|
a + b,
|
|
a - b,
|
|
a * b,
|
|
a / b,
|
|
a | b,
|
|
a & b,
|
|
a ^ b,
|
|
a >> b,
|
|
a << b,
|
|
]
|
|
assert m.test_number_protocol(a, b) == li
|
|
|
|
|
|
def test_list_slicing():
|
|
li = list(range(100))
|
|
assert li[::2] == m.test_list_slicing(li)
|
|
|
|
|
|
def test_issue2361():
|
|
# See issue #2361
|
|
assert m.issue2361_str_implicit_copy_none() == "None"
|
|
with pytest.raises(TypeError) as excinfo:
|
|
assert m.issue2361_dict_implicit_copy_none()
|
|
assert "NoneType" in str(excinfo.value)
|
|
assert "iterable" in str(excinfo.value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"method, args, fmt, expected_view",
|
|
[
|
|
(m.test_memoryview_object, (b"red",), "B", b"red"),
|
|
(m.test_memoryview_buffer_info, (b"green",), "B", b"green"),
|
|
(m.test_memoryview_from_buffer, (False,), "h", [3, 1, 4, 1, 5]),
|
|
(m.test_memoryview_from_buffer, (True,), "H", [2, 7, 1, 8]),
|
|
(m.test_memoryview_from_buffer_nativeformat, (), "@i", [4, 7, 5]),
|
|
],
|
|
)
|
|
def test_memoryview(method, args, fmt, expected_view):
|
|
view = method(*args)
|
|
assert isinstance(view, memoryview)
|
|
assert view.format == fmt
|
|
assert list(view) == list(expected_view)
|
|
|
|
|
|
@pytest.mark.xfail("env.PYPY", reason="getrefcount is not available")
|
|
@pytest.mark.parametrize(
|
|
"method",
|
|
[
|
|
m.test_memoryview_object,
|
|
m.test_memoryview_buffer_info,
|
|
],
|
|
)
|
|
def test_memoryview_refcount(method):
|
|
buf = b"\x0a\x0b\x0c\x0d"
|
|
ref_before = sys.getrefcount(buf)
|
|
view = method(buf)
|
|
ref_after = sys.getrefcount(buf)
|
|
assert ref_before < ref_after
|
|
assert list(view) == list(buf)
|
|
|
|
|
|
def test_memoryview_from_buffer_empty_shape():
|
|
view = m.test_memoryview_from_buffer_empty_shape()
|
|
assert isinstance(view, memoryview)
|
|
assert view.format == "B"
|
|
assert bytes(view) == b""
|
|
|
|
|
|
def test_test_memoryview_from_buffer_invalid_strides():
|
|
with pytest.raises(RuntimeError):
|
|
m.test_memoryview_from_buffer_invalid_strides()
|
|
|
|
|
|
def test_test_memoryview_from_buffer_nullptr():
|
|
with pytest.raises(ValueError):
|
|
m.test_memoryview_from_buffer_nullptr()
|
|
|
|
|
|
def test_memoryview_from_memory():
|
|
view = m.test_memoryview_from_memory()
|
|
assert isinstance(view, memoryview)
|
|
assert view.format == "B"
|
|
assert bytes(view) == b"\xff\xe1\xab\x37"
|
|
|
|
|
|
def test_builtin_functions():
|
|
assert m.get_len([i for i in range(42)]) == 42
|
|
with pytest.raises(TypeError) as exc_info:
|
|
m.get_len(i for i in range(42))
|
|
assert str(exc_info.value) in [
|
|
"object of type 'generator' has no len()",
|
|
"'generator' has no length",
|
|
] # PyPy
|
|
|
|
|
|
def test_isinstance_string_types():
|
|
assert m.isinstance_pybind11_bytes(b"")
|
|
assert not m.isinstance_pybind11_bytes("")
|
|
|
|
assert m.isinstance_pybind11_str("")
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert m.isinstance_pybind11_str(b"")
|
|
else:
|
|
assert not m.isinstance_pybind11_str(b"")
|
|
|
|
|
|
def test_pass_bytes_or_unicode_to_string_types():
|
|
assert m.pass_to_pybind11_bytes(b"Bytes") == 5
|
|
with pytest.raises(TypeError):
|
|
m.pass_to_pybind11_bytes("Str")
|
|
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert m.pass_to_pybind11_str(b"Bytes") == 5
|
|
else:
|
|
with pytest.raises(TypeError):
|
|
m.pass_to_pybind11_str(b"Bytes")
|
|
assert m.pass_to_pybind11_str("Str") == 3
|
|
|
|
assert m.pass_to_std_string(b"Bytes") == 5
|
|
assert m.pass_to_std_string("Str") == 3
|
|
|
|
malformed_utf8 = b"\x80"
|
|
if hasattr(m, "PYBIND11_STR_LEGACY_PERMISSIVE"):
|
|
assert m.pass_to_pybind11_str(malformed_utf8) == 1
|
|
else:
|
|
with pytest.raises(TypeError):
|
|
m.pass_to_pybind11_str(malformed_utf8)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"create_weakref, create_weakref_with_callback",
|
|
[
|
|
(m.weakref_from_handle, m.weakref_from_handle_and_function),
|
|
(m.weakref_from_object, m.weakref_from_object_and_function),
|
|
],
|
|
)
|
|
def test_weakref(create_weakref, create_weakref_with_callback):
|
|
from weakref import getweakrefcount
|
|
|
|
# Apparently, you cannot weakly reference an object()
|
|
class WeaklyReferenced:
|
|
pass
|
|
|
|
callback_called = False
|
|
|
|
def callback(wr):
|
|
nonlocal callback_called
|
|
callback_called = True
|
|
|
|
obj = WeaklyReferenced()
|
|
assert getweakrefcount(obj) == 0
|
|
wr = create_weakref(obj)
|
|
assert getweakrefcount(obj) == 1
|
|
|
|
obj = WeaklyReferenced()
|
|
assert getweakrefcount(obj) == 0
|
|
wr = create_weakref_with_callback(obj, callback) # noqa: F841
|
|
assert getweakrefcount(obj) == 1
|
|
assert not callback_called
|
|
del obj
|
|
pytest.gc_collect()
|
|
assert callback_called
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"create_weakref, has_callback",
|
|
[
|
|
(m.weakref_from_handle, False),
|
|
(m.weakref_from_object, False),
|
|
(m.weakref_from_handle_and_function, True),
|
|
(m.weakref_from_object_and_function, True),
|
|
],
|
|
)
|
|
def test_weakref_err(create_weakref, has_callback):
|
|
class C:
|
|
__slots__ = []
|
|
|
|
def callback(_):
|
|
pass
|
|
|
|
ob = C()
|
|
# Should raise TypeError on CPython
|
|
with pytest.raises(TypeError) if not env.PYPY else contextlib.nullcontext():
|
|
if has_callback:
|
|
_ = create_weakref(ob, callback)
|
|
else:
|
|
_ = create_weakref(ob)
|
|
|
|
|
|
def test_cpp_iterators():
|
|
assert m.tuple_iterator() == 12
|
|
assert m.dict_iterator() == 305 + 711
|
|
assert m.passed_iterator(iter((-7, 3))) == -4
|
|
|
|
|
|
def test_implementation_details():
|
|
lst = [39, 43, 92, 49, 22, 29, 93, 98, 26, 57, 8]
|
|
tup = tuple(lst)
|
|
assert m.sequence_item_get_ssize_t(lst) == 43
|
|
assert m.sequence_item_set_ssize_t(lst) is None
|
|
assert lst[1] == "peppa"
|
|
assert m.sequence_item_get_size_t(lst) == 92
|
|
assert m.sequence_item_set_size_t(lst) is None
|
|
assert lst[2] == "george"
|
|
assert m.list_item_get_ssize_t(lst) == 49
|
|
assert m.list_item_set_ssize_t(lst) is None
|
|
assert lst[3] == "rebecca"
|
|
assert m.list_item_get_size_t(lst) == 22
|
|
assert m.list_item_set_size_t(lst) is None
|
|
assert lst[4] == "richard"
|
|
assert m.tuple_item_get_ssize_t(tup) == 29
|
|
assert m.tuple_item_set_ssize_t() == ("emely", "edmond")
|
|
assert m.tuple_item_get_size_t(tup) == 93
|
|
assert m.tuple_item_set_size_t() == ("candy", "cat")
|
|
|
|
|
|
def test_external_float_():
|
|
r1 = m.square_float_(2.0)
|
|
assert r1 == 4.0
|
|
|
|
|
|
def test_tuple_rvalue_getter():
|
|
pop = 1000
|
|
tup = tuple(range(pop))
|
|
m.tuple_rvalue_getter(tup)
|
|
|
|
|
|
def test_list_rvalue_getter():
|
|
pop = 1000
|
|
my_list = list(range(pop))
|
|
m.list_rvalue_getter(my_list)
|
|
|
|
|
|
def test_populate_dict_rvalue():
|
|
pop = 1000
|
|
my_dict = {i: i for i in range(pop)}
|
|
assert m.populate_dict_rvalue(pop) == my_dict
|
|
|
|
|
|
def test_populate_obj_str_attrs():
|
|
pop = 1000
|
|
o = types.SimpleNamespace(**{str(i): i for i in range(pop)})
|
|
new_o = m.populate_obj_str_attrs(o, pop)
|
|
new_attrs = {k: v for k, v in new_o.__dict__.items() if not k.startswith("_")}
|
|
assert all(isinstance(v, str) for v in new_attrs.values())
|
|
assert len(new_attrs) == pop
|