@@ -1797,22 +1797,18 @@ void keep_alive(PyObject *nurse, PyObject *patient) {
1797
1797
Py_INCREF (patient);
1798
1798
((nb_inst *) nurse)->clear_keep_alive = true ;
1799
1799
} else {
1800
+ #if !defined(NB_DISABLE_INTEROP)
1801
+ if (pymb_binding *binding = pymb_get_binding (nurse);
1802
+ binding && binding->framework ->keep_alive (nurse, patient, nullptr ))
1803
+ return ;
1804
+ #endif
1800
1805
PyObject *callback =
1801
1806
PyCFunction_New (&keep_alive_callback_def, patient);
1802
1807
1803
1808
PyObject *weakref = PyWeakref_NewRef (nurse, callback);
1804
1809
if (!weakref) {
1805
1810
Py_DECREF (callback);
1806
1811
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
1816
1812
raise (" nanobind::detail::keep_alive(): could not create a weak "
1817
1813
" reference! Likely, the 'nurse' argument you specified is not "
1818
1814
" a weak-referenceable type!" );
@@ -1830,7 +1826,8 @@ void keep_alive(PyObject *nurse, void *payload,
1830
1826
void (*callback)(void *) noexcept ) noexcept {
1831
1827
check (nurse, " nanobind::detail::keep_alive(): 'nurse' is undefined!" );
1832
1828
1833
- if (nb_type_check ((PyObject *) Py_TYPE (nurse))) {
1829
+ PyObject *nurse_ty = (PyObject *) Py_TYPE (nurse);
1830
+ if (nb_type_check (nurse_ty)) {
1834
1831
#if defined(NB_FREE_THREADED)
1835
1832
nb_shard &shard = internals->shard (inst_ptr ((nb_inst *) nurse));
1836
1833
lock_shard guard (shard);
@@ -1850,6 +1847,11 @@ void keep_alive(PyObject *nurse, void *payload,
1850
1847
1851
1848
((nb_inst *) nurse)->clear_keep_alive = true ;
1852
1849
} else {
1850
+ #if !defined(NB_DISABLE_INTEROP)
1851
+ if (pymb_binding *binding = pymb_get_binding (nurse_ty);
1852
+ binding && binding->framework ->keep_alive (nurse, payload, callback))
1853
+ return ;
1854
+ #endif
1853
1855
PyObject *patient = capsule_new (payload, nullptr , callback);
1854
1856
keep_alive (nurse, patient);
1855
1857
Py_DECREF (patient);
@@ -1957,36 +1959,64 @@ PyObject *nb_type_put_foreign(nb_internals *internals_,
1957
1959
bool *is_new) noexcept {
1958
1960
struct capture {
1959
1961
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)};
1962
+ pymb_rv_policy rvp = pymb_rv_policy_none; // conservative default
1963
+ struct pymb_to_python_feedback feedback{};
1964
+ struct pymb_framework *used_framework = nullptr ;
1965
+ } cap{value};
1966
+
1967
+ switch (rvp) {
1968
+ case rv_policy::reference_internal:
1969
+ if (!cleanup || !cleanup->self ())
1970
+ return nullptr ;
1971
+ cap.rvp = pymb_rv_policy_share_ownership;
1972
+ break ;
1973
+ case rv_policy::reference:
1974
+ if (is_new) {
1975
+ cap.rvp = pymb_rv_policy_share_ownership;
1976
+ break ;
1977
+ }
1978
+ [[fallthrough]];
1979
+ case rv_policy::take_ownership:
1980
+ case rv_policy::copy:
1981
+ case rv_policy::move:
1982
+ case rv_policy::none:
1983
+ cap.rvp = (pymb_rv_policy) rvp;
1984
+ break ;
1985
+ case rv_policy::automatic:
1986
+ case rv_policy::automatic_reference:
1987
+ check (false ,
1988
+ " nb_type_put_foreign(): automatic rvp should have been "
1989
+ " converted to a different one before reaching here!" );
1990
+ break ;
1991
+ }
1965
1992
1966
1993
auto attempt = +[](void *closure, pymb_binding *binding) -> void * {
1967
1994
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
- }
1995
+ cap.used_framework = binding->framework ;
1977
1996
return binding->framework ->to_python (
1978
- binding, cap.value , (pymb_rv_policy) (uint8_t ) cap.rvp ,
1979
- cap.parent );
1997
+ binding, cap.value , cap.rvp , &cap.feedback );
1980
1998
};
1981
1999
1982
- void *result = nullptr ;
2000
+ void *result_v = nullptr ;
1983
2001
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);
2002
+ result_v = nb_type_try_foreign (internals_, cpp_type_p, attempt, &cap);
2003
+ if (!result_v)
2004
+ result_v = nb_type_try_foreign (internals_, cpp_type, attempt, &cap);
2005
+
2006
+ PyObject *result = (PyObject *) result_v;
1987
2007
if (is_new)
1988
- *is_new = cap.is_new ;
1989
- return (PyObject *) result;
2008
+ *is_new = cap.feedback .is_new ;
2009
+ if (result && rvp == rv_policy::reference_internal && cap.feedback .is_new &&
2010
+ !cap.used_framework ->keep_alive (result, cleanup->self (), nullptr )) {
2011
+ try {
2012
+ keep_alive (result, cleanup->self ());
2013
+ } catch (python_error& exc) {
2014
+ exc.restore ();
2015
+ Py_DECREF (result);
2016
+ return nullptr ;
2017
+ }
2018
+ }
2019
+ return result;
1990
2020
}
1991
2021
#endif
1992
2022
0 commit comments