mirror of
https://github.com/RYDE-WORK/pybind11.git
synced 2026-01-19 13:13:19 +08:00
* chore(deps): update pre-commit hooks updates: - [github.com/pre-commit/mirrors-clang-format: v19.1.6 → v19.1.7](https://github.com/pre-commit/mirrors-clang-format/compare/v19.1.6...v19.1.7) - [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.4) - [github.com/codespell-project/codespell: v2.3.0 → v2.4.1](https://github.com/codespell-project/codespell/compare/v2.3.0...v2.4.1) - [github.com/PyCQA/pylint: v3.3.3 → v3.3.4](https://github.com/PyCQA/pylint/compare/v3.3.3...v3.3.4) - [github.com/python-jsonschema/check-jsonschema: 0.30.0 → 0.31.1](https://github.com/python-jsonschema/check-jsonschema/compare/0.30.0...0.31.1) * style: pre-commit fixes * Rename `lins` to `lst` in test_pytypes.py to resolve codespell errors: ``` codespell................................................................Failed - hook id: codespell - exit code: 65 tests/test_pytypes.py:55: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:56: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:58: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:70: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:71: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:72: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:73: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:74: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:75: lins ==> lines, links, lions, loins, limns tests/test_pytypes.py:76: lins ==> lines, links, lions, loins, limns ``` --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
1330 lines
36 KiB
Python
1330 lines
36 KiB
Python
from __future__ import annotations
|
|
|
|
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_obj_class_name():
|
|
assert m.obj_class_name(None) == "NoneType"
|
|
assert m.obj_class_name(list) == "list"
|
|
assert m.obj_class_name([]) == "list"
|
|
|
|
|
|
def test_handle_from_move_only_type_with_operator_PyObject():
|
|
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"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("pytype", "from_iter_func"),
|
|
[
|
|
(frozenset, m.get_frozenset_from_iterable),
|
|
(list, m.get_list_from_iterable),
|
|
(set, m.get_set_from_iterable),
|
|
(tuple, m.get_tuple_from_iterable),
|
|
],
|
|
)
|
|
def test_from_iterable(pytype, from_iter_func):
|
|
my_iter = iter(range(10))
|
|
s = from_iter_func(my_iter)
|
|
assert type(s) == pytype
|
|
assert s == pytype(range(10))
|
|
|
|
|
|
def test_iterable(doc):
|
|
assert doc(m.get_iterable) == "get_iterable() -> Iterable"
|
|
lst = [1, 2, 3]
|
|
i = m.get_first_item_from_iterable(lst)
|
|
assert i == 1
|
|
i = m.get_second_item_from_iterable(lst)
|
|
assert i == 2
|
|
|
|
|
|
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() == []
|
|
lst = [1, 2]
|
|
m.list_insert_ssize_t(lst)
|
|
assert lst == [1, 83, 2]
|
|
m.list_insert_size_t(lst)
|
|
assert lst == [1, 83, 2, 57]
|
|
m.list_clear(lst)
|
|
assert lst == []
|
|
|
|
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(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: Union[set, frozenset]) -> 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}
|
|
|
|
|
|
class CustomContains:
|
|
d = {"key": None}
|
|
|
|
def __contains__(self, m):
|
|
return m in self.d
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("arg", "func"),
|
|
[
|
|
(set(), m.anyset_contains),
|
|
({}, m.dict_contains),
|
|
(CustomContains(), m.obj_contains),
|
|
],
|
|
)
|
|
@pytest.mark.xfail("env.PYPY and sys.pypy_version_info < (7, 3, 10)", strict=False)
|
|
def test_unhashable_exceptions(arg, func):
|
|
class Unhashable:
|
|
__hash__ = None
|
|
|
|
with pytest.raises(TypeError) as exc_info:
|
|
func(arg, Unhashable())
|
|
assert "unhashable type:" in str(exc_info.value)
|
|
|
|
|
|
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)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"func",
|
|
[
|
|
m.str_from_bytes_input,
|
|
m.str_from_cstr_input,
|
|
m.str_from_std_string_input,
|
|
],
|
|
)
|
|
@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side")
|
|
def test_surrogate_pairs_unicode_error(func):
|
|
input_str = "\ud83d\ude4f".encode("utf-8", "surrogatepass")
|
|
with pytest.raises(UnicodeDecodeError):
|
|
func(input_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():
|
|
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_capsule_with_destructor_3()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule
|
|
destructing capsule: 1233
|
|
original name: oname
|
|
"""
|
|
)
|
|
|
|
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')
|
|
"""
|
|
)
|
|
|
|
with capture:
|
|
a = m.return_capsule_with_explicit_nullptr_dtor()
|
|
del a
|
|
pytest.gc_collect()
|
|
assert (
|
|
capture.unordered
|
|
== """
|
|
creating capsule with explicit nullptr dtor
|
|
"""
|
|
)
|
|
|
|
|
|
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(()) == (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")
|
|
|
|
|
|
@pytest.mark.xfail("env.GRAALPY", reason="TODO should be fixed on GraalPy side")
|
|
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}"
|
|
|
|
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 "'1' 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):
|
|
# Avoiding a literal to avoid an immortal object in free-threaded builds
|
|
buf = "\x0a\x0b\x0c\x0d".encode("ascii")
|
|
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(list(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.skipif("env.GRAALPY", reason="Cannot reliably trigger GC")
|
|
@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(_):
|
|
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
|
|
cm = pytest.raises(TypeError)
|
|
if env.PYPY or env.GRAALPY:
|
|
cm = contextlib.nullcontext()
|
|
with cm:
|
|
_ = create_weakref(ob, callback) if has_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
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("a", "b"),
|
|
[("foo", "bar"), (1, 2), (1.0, 2.0), (list(range(3)), list(range(3, 6)))],
|
|
)
|
|
def test_inplace_append(a, b):
|
|
expected = a + b
|
|
assert m.inplace_append(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("a", "b"), [(3, 2), (3.0, 2.0), (set(range(3)), set(range(2)))]
|
|
)
|
|
def test_inplace_subtract(a, b):
|
|
expected = a - b
|
|
assert m.inplace_subtract(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(("a", "b"), [(3, 2), (3.0, 2.0), ([1], 3)])
|
|
def test_inplace_multiply(a, b):
|
|
expected = a * b
|
|
assert m.inplace_multiply(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(("a", "b"), [(6, 3), (6.0, 3.0)])
|
|
def test_inplace_divide(a, b):
|
|
expected = a / b
|
|
assert m.inplace_divide(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("a", "b"),
|
|
[
|
|
(False, True),
|
|
(
|
|
set(),
|
|
{
|
|
1,
|
|
},
|
|
),
|
|
],
|
|
)
|
|
def test_inplace_or(a, b):
|
|
expected = a | b
|
|
assert m.inplace_or(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("a", "b"),
|
|
[
|
|
(True, False),
|
|
(
|
|
{1, 2, 3},
|
|
{
|
|
1,
|
|
},
|
|
),
|
|
],
|
|
)
|
|
def test_inplace_and(a, b):
|
|
expected = a & b
|
|
assert m.inplace_and(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(("a", "b"), [(8, 1), (-3, 2)])
|
|
def test_inplace_lshift(a, b):
|
|
expected = a << b
|
|
assert m.inplace_lshift(a, b) == expected
|
|
|
|
|
|
@pytest.mark.parametrize(("a", "b"), [(8, 1), (-2, 2)])
|
|
def test_inplace_rshift(a, b):
|
|
expected = a >> b
|
|
assert m.inplace_rshift(a, b) == expected
|
|
|
|
|
|
def test_tuple_nonempty_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_tuple_float_str)
|
|
== "annotate_tuple_float_str(arg0: tuple[float, str]) -> None"
|
|
)
|
|
|
|
|
|
def test_tuple_empty_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_tuple_empty) == "annotate_tuple_empty(arg0: tuple[()]) -> None"
|
|
)
|
|
|
|
|
|
def test_tuple_variable_length_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_tuple_variable_length)
|
|
== "annotate_tuple_variable_length(arg0: tuple[float, ...]) -> None"
|
|
)
|
|
|
|
|
|
def test_dict_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_dict_str_int)
|
|
== "annotate_dict_str_int(arg0: dict[str, int]) -> None"
|
|
)
|
|
|
|
|
|
def test_list_annotations(doc):
|
|
assert doc(m.annotate_list_int) == "annotate_list_int(arg0: list[int]) -> None"
|
|
|
|
|
|
def test_set_annotations(doc):
|
|
assert doc(m.annotate_set_str) == "annotate_set_str(arg0: set[str]) -> None"
|
|
|
|
|
|
def test_iterable_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_iterable_str)
|
|
== "annotate_iterable_str(arg0: Iterable[str]) -> None"
|
|
)
|
|
|
|
|
|
def test_iterator_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_iterator_int)
|
|
== "annotate_iterator_int(arg0: Iterator[int]) -> None"
|
|
)
|
|
|
|
|
|
def test_fn_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_fn)
|
|
== "annotate_fn(arg0: Callable[[list[str], str], int]) -> None"
|
|
)
|
|
|
|
|
|
def test_fn_return_only(doc):
|
|
assert (
|
|
doc(m.annotate_fn_only_return)
|
|
== "annotate_fn_only_return(arg0: Callable[..., int]) -> None"
|
|
)
|
|
|
|
|
|
def test_type_annotation(doc):
|
|
assert doc(m.annotate_type) == "annotate_type(arg0: type[int]) -> type"
|
|
|
|
|
|
def test_union_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_union)
|
|
== "annotate_union(arg0: list[Union[str, int, object]], arg1: str, arg2: int, arg3: object) -> list[Union[str, int, object]]"
|
|
)
|
|
|
|
|
|
def test_union_typing_only(doc):
|
|
assert (
|
|
doc(m.union_typing_only)
|
|
== "union_typing_only(arg0: list[Union[str]]) -> list[Union[int]]"
|
|
)
|
|
|
|
|
|
def test_union_object_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_union_to_object)
|
|
== "annotate_union_to_object(arg0: Union[int, str]) -> object"
|
|
)
|
|
|
|
|
|
def test_optional_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_optional)
|
|
== "annotate_optional(arg0: list) -> list[Optional[str]]"
|
|
)
|
|
|
|
|
|
def test_type_guard_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_type_guard)
|
|
== "annotate_type_guard(arg0: object) -> TypeGuard[str]"
|
|
)
|
|
|
|
|
|
def test_type_is_annotations(doc):
|
|
assert doc(m.annotate_type_is) == "annotate_type_is(arg0: object) -> TypeIs[str]"
|
|
|
|
|
|
def test_no_return_annotation(doc):
|
|
assert doc(m.annotate_no_return) == "annotate_no_return() -> NoReturn"
|
|
|
|
|
|
def test_never_annotation(doc):
|
|
assert doc(m.annotate_never) == "annotate_never() -> Never"
|
|
|
|
|
|
def test_optional_object_annotations(doc):
|
|
assert (
|
|
doc(m.annotate_optional_to_object)
|
|
== "annotate_optional_to_object(arg0: Optional[int]) -> object"
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
|
reason="C++20 non-type template args feature not available.",
|
|
)
|
|
def test_literal(doc):
|
|
assert (
|
|
doc(m.annotate_literal)
|
|
== 'annotate_literal(arg0: Literal[26, 0x1A, "hello world", b"hello world", u"hello world", True, Color.RED, None]) -> object'
|
|
)
|
|
# The characters !, @, %, {, } and -> are used in the signature parser as special characters, but Literal should escape those for the parser to work.
|
|
assert (
|
|
doc(m.identity_literal_exclamation)
|
|
== 'identity_literal_exclamation(arg0: Literal["!"]) -> Literal["!"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_at)
|
|
== 'identity_literal_at(arg0: Literal["@"]) -> Literal["@"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_percent)
|
|
== 'identity_literal_percent(arg0: Literal["%"]) -> Literal["%"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_curly_open)
|
|
== 'identity_literal_curly_open(arg0: Literal["{"]) -> Literal["{"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_curly_close)
|
|
== 'identity_literal_curly_close(arg0: Literal["}"]) -> Literal["}"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_arrow_with_io_name)
|
|
== 'identity_literal_arrow_with_io_name(arg0: Literal["->"], arg1: Union[float, int]) -> Literal["->"]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_arrow_with_callable)
|
|
== 'identity_literal_arrow_with_callable(arg0: Callable[[Literal["->"], Union[float, int]], float]) -> Callable[[Literal["->"], Union[float, int]], float]'
|
|
)
|
|
assert (
|
|
doc(m.identity_literal_all_special_chars)
|
|
== 'identity_literal_all_special_chars(arg0: Literal["!@!!->{%}"]) -> Literal["!@!!->{%}"]'
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined_PYBIND11_TYPING_H_HAS_STRING_LITERAL,
|
|
reason="C++20 non-type template args feature not available.",
|
|
)
|
|
def test_typevar(doc):
|
|
assert (
|
|
doc(m.annotate_generic_containers)
|
|
== "annotate_generic_containers(arg0: list[T]) -> list[V]"
|
|
)
|
|
|
|
assert doc(m.annotate_listT_to_T) == "annotate_listT_to_T(arg0: list[T]) -> T"
|
|
|
|
assert doc(m.annotate_object_to_T) == "annotate_object_to_T(arg0: object) -> T"
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
|
reason="<ranges> not available.",
|
|
)
|
|
@pytest.mark.parametrize(
|
|
("tested_tuple", "expected"),
|
|
[((1,), [2]), ((3, 4), [4, 5]), ((7, 8, 9), [8, 9, 10])],
|
|
)
|
|
def test_tuple_ranges(tested_tuple, expected):
|
|
assert m.tuple_iterator_default_initialization()
|
|
assert m.transform_tuple_plus_one(tested_tuple) == expected
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
|
reason="<ranges> not available.",
|
|
)
|
|
@pytest.mark.parametrize(
|
|
("tested_list", "expected"), [([1], [2]), ([3, 4], [4, 5]), ([7, 8, 9], [8, 9, 10])]
|
|
)
|
|
def test_list_ranges(tested_list, expected):
|
|
assert m.list_iterator_default_initialization()
|
|
assert m.transform_list_plus_one(tested_list) == expected
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined_PYBIND11_TEST_PYTYPES_HAS_RANGES,
|
|
reason="<ranges> not available.",
|
|
)
|
|
@pytest.mark.parametrize(
|
|
("tested_dict", "expected"),
|
|
[
|
|
({1: 2}, [(2, 3)]),
|
|
({3: 4, 5: 6}, [(4, 5), (6, 7)]),
|
|
({7: 8, 9: 10, 11: 12}, [(8, 9), (10, 11), (12, 13)]),
|
|
],
|
|
)
|
|
def test_dict_ranges(tested_dict, expected):
|
|
assert m.dict_iterator_default_initialization()
|
|
assert m.transform_dict_plus_one(tested_dict) == expected
|
|
|
|
|
|
# https://docs.python.org/3/howto/annotations.html#accessing-the-annotations-dict-of-an-object-in-python-3-9-and-older
|
|
def get_annotations_helper(o):
|
|
if isinstance(o, type):
|
|
return o.__dict__.get("__annotations__", None)
|
|
return getattr(o, "__annotations__", None)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined___cpp_inline_variables,
|
|
reason="C++17 feature __cpp_inline_variables not available.",
|
|
)
|
|
def test_module_attribute_types() -> None:
|
|
module_annotations = get_annotations_helper(m)
|
|
|
|
assert module_annotations["list_int"] == "list[int]"
|
|
assert module_annotations["set_str"] == "set[str]"
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined___cpp_inline_variables,
|
|
reason="C++17 feature __cpp_inline_variables not available.",
|
|
)
|
|
@pytest.mark.skipif(
|
|
sys.version_info < (3, 10),
|
|
reason="get_annotations function does not exist until Python3.10",
|
|
)
|
|
def test_get_annotations_compliance() -> None:
|
|
from inspect import get_annotations
|
|
|
|
module_annotations = get_annotations(m)
|
|
|
|
assert module_annotations["list_int"] == "list[int]"
|
|
assert module_annotations["set_str"] == "set[str]"
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined___cpp_inline_variables,
|
|
reason="C++17 feature __cpp_inline_variables not available.",
|
|
)
|
|
def test_class_attribute_types() -> None:
|
|
empty_annotations = get_annotations_helper(m.EmptyAnnotationClass)
|
|
static_annotations = get_annotations_helper(m.Static)
|
|
instance_annotations = get_annotations_helper(m.Instance)
|
|
|
|
assert empty_annotations is None
|
|
assert static_annotations["x"] == "ClassVar[float]"
|
|
assert static_annotations["dict_str_int"] == "ClassVar[dict[str, int]]"
|
|
|
|
assert m.Static.x == 1.0
|
|
|
|
m.Static.x = 3.0
|
|
static = m.Static()
|
|
assert static.x == 3.0
|
|
|
|
static.dict_str_int["hi"] = 3
|
|
assert m.Static().dict_str_int == {"hi": 3}
|
|
|
|
assert instance_annotations["y"] == "float"
|
|
instance1 = m.Instance()
|
|
instance1.y = 4.0
|
|
|
|
instance2 = m.Instance()
|
|
instance2.y = 5.0
|
|
|
|
assert instance1.y != instance2.y
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined___cpp_inline_variables,
|
|
reason="C++17 feature __cpp_inline_variables not available.",
|
|
)
|
|
def test_redeclaration_attr_with_type_hint() -> None:
|
|
obj = m.Instance()
|
|
m.attr_with_type_hint_float_x(obj)
|
|
assert get_annotations_helper(obj)["x"] == "float"
|
|
with pytest.raises(
|
|
RuntimeError, match=r'^__annotations__\["x"\] was set already\.$'
|
|
):
|
|
m.attr_with_type_hint_float_x(obj)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not m.defined___cpp_inline_variables,
|
|
reason="C++17 feature __cpp_inline_variables not available.",
|
|
)
|
|
def test_final_annotation() -> None:
|
|
module_annotations = get_annotations_helper(m)
|
|
assert module_annotations["CONST_INT"] == "Final[int]"
|
|
|
|
|
|
def test_arg_return_type_hints(doc):
|
|
assert doc(m.half_of_number) == "half_of_number(arg0: Union[float, int]) -> float"
|
|
assert (
|
|
doc(m.half_of_number_convert)
|
|
== "half_of_number_convert(x: Union[float, int]) -> float"
|
|
)
|
|
assert (
|
|
doc(m.half_of_number_noconvert) == "half_of_number_noconvert(x: float) -> float"
|
|
)
|
|
assert m.half_of_number(2.0) == 1.0
|
|
assert m.half_of_number(2) == 1.0
|
|
assert m.half_of_number(0) == 0
|
|
assert isinstance(m.half_of_number(0), float)
|
|
assert not isinstance(m.half_of_number(0), int)
|
|
# std::vector<T>
|
|
assert (
|
|
doc(m.half_of_number_vector)
|
|
== "half_of_number_vector(arg0: list[Union[float, int]]) -> list[float]"
|
|
)
|
|
# Tuple<T, T>
|
|
assert (
|
|
doc(m.half_of_number_tuple)
|
|
== "half_of_number_tuple(arg0: tuple[Union[float, int], Union[float, int]]) -> tuple[float, float]"
|
|
)
|
|
# Tuple<T, ...>
|
|
assert (
|
|
doc(m.half_of_number_tuple_ellipsis)
|
|
== "half_of_number_tuple_ellipsis(arg0: tuple[Union[float, int], ...]) -> tuple[float, ...]"
|
|
)
|
|
# Dict<K, V>
|
|
assert (
|
|
doc(m.half_of_number_dict)
|
|
== "half_of_number_dict(arg0: dict[str, Union[float, int]]) -> dict[str, float]"
|
|
)
|
|
# List<T>
|
|
assert (
|
|
doc(m.half_of_number_list)
|
|
== "half_of_number_list(arg0: list[Union[float, int]]) -> list[float]"
|
|
)
|
|
# List<List<T>>
|
|
assert (
|
|
doc(m.half_of_number_nested_list)
|
|
== "half_of_number_nested_list(arg0: list[list[Union[float, int]]]) -> list[list[float]]"
|
|
)
|
|
# Set<T>
|
|
assert (
|
|
doc(m.identity_set)
|
|
== "identity_set(arg0: set[Union[float, int]]) -> set[float]"
|
|
)
|
|
# Iterable<T>
|
|
assert (
|
|
doc(m.identity_iterable)
|
|
== "identity_iterable(arg0: Iterable[Union[float, int]]) -> Iterable[float]"
|
|
)
|
|
# Iterator<T>
|
|
assert (
|
|
doc(m.identity_iterator)
|
|
== "identity_iterator(arg0: Iterator[Union[float, int]]) -> Iterator[float]"
|
|
)
|
|
# Callable<R(A)> identity
|
|
assert (
|
|
doc(m.identity_callable)
|
|
== "identity_callable(arg0: Callable[[Union[float, int]], float]) -> Callable[[Union[float, int]], float]"
|
|
)
|
|
# Callable<R(...)> identity
|
|
assert (
|
|
doc(m.identity_callable_ellipsis)
|
|
== "identity_callable_ellipsis(arg0: Callable[..., float]) -> Callable[..., float]"
|
|
)
|
|
# Nested Callable<R(A)> identity
|
|
assert (
|
|
doc(m.identity_nested_callable)
|
|
== "identity_nested_callable(arg0: Callable[[Callable[[Union[float, int]], float]], Callable[[Union[float, int]], float]]) -> Callable[[Callable[[Union[float, int]], float]], Callable[[Union[float, int]], float]]"
|
|
)
|
|
# Callable<R(A)>
|
|
assert (
|
|
doc(m.apply_callable)
|
|
== "apply_callable(arg0: Union[float, int], arg1: Callable[[Union[float, int]], float]) -> float"
|
|
)
|
|
# Callable<R(...)>
|
|
assert (
|
|
doc(m.apply_callable_ellipsis)
|
|
== "apply_callable_ellipsis(arg0: Union[float, int], arg1: Callable[..., float]) -> float"
|
|
)
|
|
# Union<T1, T2>
|
|
assert (
|
|
doc(m.identity_union)
|
|
== "identity_union(arg0: Union[Union[float, int], str]) -> Union[float, str]"
|
|
)
|
|
# Optional<T>
|
|
assert (
|
|
doc(m.identity_optional)
|
|
== "identity_optional(arg0: Optional[Union[float, int]]) -> Optional[float]"
|
|
)
|
|
# TypeGuard<T>
|
|
assert (
|
|
doc(m.check_type_guard)
|
|
== "check_type_guard(arg0: list[object]) -> TypeGuard[list[float]]"
|
|
)
|
|
# TypeIs<T>
|
|
assert doc(m.check_type_is) == "check_type_is(arg0: object) -> TypeIs[float]"
|