@@ -1767,7 +1767,8 @@ void keep_alive(PyObject *nurse, PyObject *patient) {
17671767 if (!patient || !nurse || nurse == Py_None || patient == Py_None)
17681768 return ;
17691769
1770- if (nb_type_check ((PyObject *) Py_TYPE (nurse))) {
1770+ PyObject *nurse_ty = (PyObject *) Py_TYPE (nurse);
1771+ if (nb_type_check (nurse_ty)) {
17711772#if defined(NB_FREE_THREADED)
17721773 nb_shard &shard = internals->shard (inst_ptr ((nb_inst *) nurse));
17731774 lock_shard guard (shard);
@@ -1797,22 +1798,18 @@ void keep_alive(PyObject *nurse, PyObject *patient) {
17971798 Py_INCREF (patient);
17981799 ((nb_inst *) nurse)->clear_keep_alive = true ;
17991800 } else {
1801+ #if !defined(NB_DISABLE_INTEROP)
1802+ if (pymb_binding *binding = pymb_get_binding (nurse_ty);
1803+ binding && binding->framework ->keep_alive (nurse, patient, nullptr ))
1804+ return ;
1805+ #endif
18001806 PyObject *callback =
18011807 PyCFunction_New (&keep_alive_callback_def, patient);
18021808
18031809 PyObject *weakref = PyWeakref_NewRef (nurse, callback);
18041810 if (!weakref) {
18051811 Py_DECREF (callback);
18061812 PyErr_Clear ();
1807- #if !defined(NB_DISABLE_INTEROP)
1808- if (pymb_binding *binding = pymb_get_binding (nurse)) {
1809- // Try a foreign framework's keep_alive as a last resort
1810- if (binding->framework ->keep_alive (nurse, patient,
1811- nullptr ) == 0 )
1812- return ;
1813- raise_python_error ();
1814- }
1815- #endif
18161813 raise (" nanobind::detail::keep_alive(): could not create a weak "
18171814 " reference! Likely, the 'nurse' argument you specified is not "
18181815 " a weak-referenceable type!" );
@@ -1830,7 +1827,8 @@ void keep_alive(PyObject *nurse, void *payload,
18301827 void (*callback)(void *) noexcept ) noexcept {
18311828 check (nurse, " nanobind::detail::keep_alive(): 'nurse' is undefined!" );
18321829
1833- if (nb_type_check ((PyObject *) Py_TYPE (nurse))) {
1830+ PyObject *nurse_ty = (PyObject *) Py_TYPE (nurse);
1831+ if (nb_type_check (nurse_ty)) {
18341832#if defined(NB_FREE_THREADED)
18351833 nb_shard &shard = internals->shard (inst_ptr ((nb_inst *) nurse));
18361834 lock_shard guard (shard);
@@ -1850,6 +1848,11 @@ void keep_alive(PyObject *nurse, void *payload,
18501848
18511849 ((nb_inst *) nurse)->clear_keep_alive = true ;
18521850 } else {
1851+ #if !defined(NB_DISABLE_INTEROP)
1852+ if (pymb_binding *binding = pymb_get_binding (nurse_ty);
1853+ binding && binding->framework ->keep_alive (nurse, payload, callback))
1854+ return ;
1855+ #endif
18531856 PyObject *patient = capsule_new (payload, nullptr , callback);
18541857 keep_alive (nurse, patient);
18551858 Py_DECREF (patient);
@@ -1957,36 +1960,64 @@ PyObject *nb_type_put_foreign(nb_internals *internals_,
19571960 bool *is_new) noexcept {
19581961 struct capture {
19591962 void *value;
1960- rv_policy rvp;
1961- PyObject *parent;
1962- bool check_new;
1963- bool is_new = false ;
1964- } cap{value, rvp, cleanup ? cleanup->self () : nullptr , bool (is_new)};
1963+ pymb_rv_policy rvp = pymb_rv_policy_none; // conservative default
1964+ struct pymb_to_python_feedback feedback{};
1965+ struct pymb_framework *used_framework = nullptr ;
1966+ } cap{value};
1967+
1968+ switch (rvp) {
1969+ case rv_policy::reference_internal:
1970+ if (!cleanup || !cleanup->self ())
1971+ return nullptr ;
1972+ cap.rvp = pymb_rv_policy_share_ownership;
1973+ break ;
1974+ case rv_policy::reference:
1975+ if (is_new) {
1976+ cap.rvp = pymb_rv_policy_share_ownership;
1977+ break ;
1978+ }
1979+ [[fallthrough]];
1980+ case rv_policy::take_ownership:
1981+ case rv_policy::copy:
1982+ case rv_policy::move:
1983+ case rv_policy::none:
1984+ cap.rvp = (pymb_rv_policy) rvp;
1985+ break ;
1986+ case rv_policy::automatic:
1987+ case rv_policy::automatic_reference:
1988+ check (false ,
1989+ " nb_type_put_foreign(): automatic rvp should have been "
1990+ " converted to a different one before reaching here!" );
1991+ break ;
1992+ }
19651993
19661994 auto attempt = +[](void *closure, pymb_binding *binding) -> void * {
19671995 capture &cap = *(capture *) closure;
1968- if (cap.check_new || cap.rvp == rv_policy::none) {
1969- PyObject* existing = binding->framework ->to_python (
1970- binding, cap.value , pymb_rv_policy_none, nullptr );
1971- if (existing || cap.rvp == rv_policy::none) {
1972- cap.is_new = false ;
1973- return existing;
1974- }
1975- cap.is_new = true ;
1976- }
1996+ cap.used_framework = binding->framework ;
19771997 return binding->framework ->to_python (
1978- binding, cap.value , (pymb_rv_policy) (uint8_t ) cap.rvp ,
1979- cap.parent );
1998+ binding, cap.value , cap.rvp , &cap.feedback );
19801999 };
19812000
1982- void *result = nullptr ;
2001+ void *result_v = nullptr ;
19832002 if (cpp_type_p && cpp_type_p != cpp_type)
1984- result = nb_type_try_foreign (internals_, cpp_type_p, attempt, &cap);
1985- if (!result)
1986- result = nb_type_try_foreign (internals_, cpp_type, attempt, &cap);
2003+ result_v = nb_type_try_foreign (internals_, cpp_type_p, attempt, &cap);
2004+ if (!result_v)
2005+ result_v = nb_type_try_foreign (internals_, cpp_type, attempt, &cap);
2006+
2007+ PyObject *result = (PyObject *) result_v;
19872008 if (is_new)
1988- *is_new = cap.is_new ;
1989- return (PyObject *) result;
2009+ *is_new = cap.feedback .is_new ;
2010+ if (result && rvp == rv_policy::reference_internal && cap.feedback .is_new &&
2011+ !cap.used_framework ->keep_alive (result, cleanup->self (), nullptr )) {
2012+ try {
2013+ keep_alive (result, cleanup->self ());
2014+ } catch (python_error& exc) {
2015+ exc.restore ();
2016+ Py_DECREF (result);
2017+ return nullptr ;
2018+ }
2019+ }
2020+ return result;
19902021}
19912022#endif
19922023
0 commit comments