|
| 1 | +"""Bound types must be weak-referenceable. |
| 2 | +
|
| 3 | +pybind11 set ``tp_weaklistoffset`` on every bound type by default, so |
| 4 | +``weakref.ref``/``proxy``/``finalize`` and ``WeakValueDictionary`` worked out of the box. |
| 5 | +nanobind opts out by default and requires ``py::is_weak_referenceable()`` at registration; without |
| 6 | +it those calls raise ``TypeError: cannot create weak reference``. This guards that regression for |
| 7 | +every publicly handed-out bound type (Connection, Relation, Expression, Type, Statement). |
| 8 | +""" |
| 9 | + |
| 10 | +import platform |
| 11 | +import weakref |
| 12 | + |
| 13 | +import pytest |
| 14 | + |
| 15 | +import duckdb |
| 16 | + |
| 17 | +pytestmark = pytest.mark.skipif( |
| 18 | + platform.system() == "Emscripten", |
| 19 | + reason="Extensions are not supported on Emscripten", |
| 20 | +) |
| 21 | + |
| 22 | + |
| 23 | +@pytest.fixture |
| 24 | +def bound_objects(): |
| 25 | + con = duckdb.connect() |
| 26 | + objs = { |
| 27 | + "Connection": con, |
| 28 | + "Relation": con.sql("SELECT 42 AS a"), |
| 29 | + "Expression": duckdb.ColumnExpression("a"), |
| 30 | + "Type": duckdb.type("INTEGER"), |
| 31 | + "Statement": con.extract_statements("SELECT 42")[0], |
| 32 | + } |
| 33 | + yield objs |
| 34 | + con.close() |
| 35 | + |
| 36 | + |
| 37 | +@pytest.mark.parametrize( |
| 38 | + "name", |
| 39 | + ["Connection", "Relation", "Expression", "Type", "Statement"], |
| 40 | +) |
| 41 | +def test_bound_type_is_weak_referenceable(bound_objects, name): |
| 42 | + obj = bound_objects[name] |
| 43 | + |
| 44 | + ref = weakref.ref(obj) |
| 45 | + assert ref() is obj |
| 46 | + |
| 47 | + weakref.proxy(obj) # must not raise |
| 48 | + |
| 49 | + finalized = [] |
| 50 | + weakref.finalize(obj, finalized.append, name) |
| 51 | + |
| 52 | + wvd = weakref.WeakValueDictionary() |
| 53 | + wvd["k"] = obj |
| 54 | + assert wvd["k"] is obj |
0 commit comments