diff --git a/include/boost/python/call.hpp b/include/boost/python/call.hpp index d80cba3c..ff74eb36 100644 --- a/include/boost/python/call.hpp +++ b/include/boost/python/call.hpp @@ -52,13 +52,22 @@ call(PyObject* callable , boost::type* = 0 ) { - converter::return_from_python converter; - return converter( + PyObject* const result = PyEval_CallFunction( callable , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil) - )); + ); + + // This conversion *must not* be done in the same expression as + // the call, because, in the special case where the result is a + // reference a Python object which was created by converting a C++ + // argument for passing to PyEval_CallFunction, its reference + // count will be 2 until the end of the full expression containing + // the conversion, and that interferes with dangling + // pointer/reference detection. + converter::return_from_python converter; + return converter(result); } # undef N diff --git a/include/boost/python/call_method.hpp b/include/boost/python/call_method.hpp index 347f866c..4e9b9373 100644 --- a/include/boost/python/call_method.hpp +++ b/include/boost/python/call_method.hpp @@ -51,14 +51,23 @@ call_method(PyObject* self, char const* name , boost::type* = 0 ) { - converter::return_from_python converter; - return converter( + PyObject* const result = PyEval_CallMethod( self , const_cast(name) , const_cast("(" BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FIXED, "O") ")") BOOST_PP_REPEAT_1ST(N, BOOST_PYTHON_FAST_ARG_TO_PYTHON_GET, nil) - )); + ); + + // This conversion *must not* be done in the same expression as + // the call, because, in the special case where the result is a + // reference a Python object which was created by converting a C++ + // argument for passing to PyEval_CallFunction, its reference + // count will be 2 until the end of the full expression containing + // the conversion, and that interferes with dangling + // pointer/reference detection. + converter::return_from_python converter; + return converter(result); } # undef N diff --git a/src/converter/from_python.cpp b/src/converter/from_python.cpp index e633debf..f0667254 100644 --- a/src/converter/from_python.cpp +++ b/src/converter/from_python.cpp @@ -218,7 +218,7 @@ namespace , char const* ref_type) { handle<> holder(source); - if (source->ob_refcnt <= 2) + if (source->ob_refcnt <= 1) { handle<> msg( ::PyString_FromFormat( diff --git a/test/callbacks.py b/test/callbacks.py index 937961fd..d5bf44d4 100644 --- a/test/callbacks.py +++ b/test/callbacks.py @@ -15,9 +15,18 @@ Once we have array conversion support, this test will fail. Er, succeed: >>> try: apply_to_string_literal(identity) -... except: pass # expected +... except ReferenceError: pass # expected ... else: print 'expected an exception!' +>>> try: apply_X_ref_handle(lambda ignored:X(42), None) +... except ReferenceError: pass # expected +... else: print 'expected an exception!' + +>>> x = X(42) +>>> x.y = X(7) +>>> apply_X_ref_handle(lambda z:z.y, x).value() +7 + >>> x = apply_X_X(identity, X(42)) >>> x.value() 42