diff options
author | Mark Spruiell <mes@zeroc.com> | 2016-12-09 15:18:08 -0800 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2016-12-09 15:18:08 -0800 |
commit | 3b7e9f99b61538e0bbd6f07deeb7f7cb12013ed5 (patch) | |
tree | a8edbf5d1043527cc50880b34ee83458ed7e4855 /python/modules/IcePy | |
parent | Merge remote-tracking branch 'origin/3.6' (diff) | |
download | ice-3b7e9f99b61538e0bbd6f07deeb7f7cb12013ed5.tar.bz2 ice-3b7e9f99b61538e0bbd6f07deeb7f7cb12013ed5.tar.xz ice-3b7e9f99b61538e0bbd6f07deeb7f7cb12013ed5.zip |
ICE-7138 - new Python AMI mapping based on futures and modified AMD mapping
Diffstat (limited to 'python/modules/IcePy')
-rw-r--r-- | python/modules/IcePy/Communicator.cpp | 59 | ||||
-rw-r--r-- | python/modules/IcePy/Connection.cpp | 46 | ||||
-rw-r--r-- | python/modules/IcePy/ObjectAdapter.cpp | 4 | ||||
-rw-r--r-- | python/modules/IcePy/Operation.cpp | 2209 | ||||
-rw-r--r-- | python/modules/IcePy/Operation.h | 56 | ||||
-rw-r--r-- | python/modules/IcePy/Proxy.cpp | 195 | ||||
-rw-r--r-- | python/modules/IcePy/Slice.cpp | 9 | ||||
-rw-r--r-- | python/modules/IcePy/Util.cpp | 59 | ||||
-rw-r--r-- | python/modules/IcePy/Util.h | 20 |
9 files changed, 1768 insertions, 889 deletions
diff --git a/python/modules/IcePy/Communicator.cpp b/python/modules/IcePy/Communicator.cpp index eb223c85853..927c3cab5d2 100644 --- a/python/modules/IcePy/Communicator.cpp +++ b/python/modules/IcePy/Communicator.cpp @@ -723,6 +723,48 @@ communicatorFlushBatchRequests(CommunicatorObject* self) extern "C" #endif static PyObject* +communicatorFlushBatchRequestsAsync(CommunicatorObject* self, PyObject* /*args*/, PyObject* /*kwds*/) +{ + assert(self->communicator); + const string op = "flushBatchRequests"; + + FlushAsyncCallbackPtr d = new FlushAsyncCallback(op); + Ice::Callback_Communicator_flushBatchRequestsPtr cb = + Ice::newCallback_Communicator_flushBatchRequests(d, &FlushAsyncCallback::exception, &FlushAsyncCallback::sent); + + Ice::AsyncResultPtr result; + + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + + result = (*self->communicator)->begin_flushBatchRequests(cb); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + PyObjectHandle asyncResultObj = createAsyncResult(result, 0, 0, self->wrapper); + if(!asyncResultObj.get()) + { + return 0; + } + + PyObjectHandle future = createFuture(op, asyncResultObj.get()); + if(!future.get()) + { + return 0; + } + d->setFuture(future.get()); + return future.release(); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* communicatorBeginFlushBatchRequests(CommunicatorObject* self, PyObject* args, PyObject* kwds) { assert(self->communicator); @@ -1608,6 +1650,8 @@ static PyMethodDef CommunicatorMethods[] = PyDoc_STR(STRCAST("setDefaultLocator(proxy) -> None")) }, { STRCAST("flushBatchRequests"), reinterpret_cast<PyCFunction>(communicatorFlushBatchRequests), METH_NOARGS, PyDoc_STR(STRCAST("flushBatchRequests() -> None")) }, + { STRCAST("flushBatchRequestsAsync"), reinterpret_cast<PyCFunction>(communicatorFlushBatchRequestsAsync), + METH_NOARGS, PyDoc_STR(STRCAST("flushBatchRequestsAsync() -> Ice.Future")) }, { STRCAST("begin_flushBatchRequests"), reinterpret_cast<PyCFunction>(communicatorBeginFlushBatchRequests), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_flushBatchRequests([_ex][, _sent]) -> Ice.AsyncResult")) }, @@ -1734,8 +1778,19 @@ IcePy::getCommunicatorWrapper(const Ice::CommunicatorPtr& communicator) CommunicatorMap::iterator p = _communicatorMap.find(communicator); assert(p != _communicatorMap.end()); CommunicatorObject* obj = reinterpret_cast<CommunicatorObject*>(p->second); - Py_INCREF(obj->wrapper); - return obj->wrapper; + if(obj->wrapper) + { + Py_INCREF(obj->wrapper); + return obj->wrapper; + } + else + { + // + // Communicator must have been destroyed already. + // + Py_INCREF(Py_None); + return Py_None; + } } extern "C" diff --git a/python/modules/IcePy/Connection.cpp b/python/modules/IcePy/Connection.cpp index 2f5998bffc9..ec1e4f8db68 100644 --- a/python/modules/IcePy/Connection.cpp +++ b/python/modules/IcePy/Connection.cpp @@ -379,6 +379,50 @@ connectionFlushBatchRequests(ConnectionObject* self) extern "C" #endif static PyObject* +connectionFlushBatchRequestsAsync(ConnectionObject* self, PyObject* /*args*/, PyObject* /*kwds*/) +{ + assert(self->connection); + const string op = "flushBatchRequests"; + + FlushAsyncCallbackPtr d = new FlushAsyncCallback(op); + Ice::Callback_Connection_flushBatchRequestsPtr cb = + Ice::newCallback_Connection_flushBatchRequests(d, &FlushAsyncCallback::exception, &FlushAsyncCallback::sent); + + Ice::AsyncResultPtr result; + + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + + result = (*self->connection)->begin_flushBatchRequests(cb); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + PyObjectHandle communicatorObj = getCommunicatorWrapper(*self->communicator); + PyObjectHandle asyncResultObj = + createAsyncResult(result, 0, reinterpret_cast<PyObject*>(self), communicatorObj.get()); + if(!asyncResultObj.get()) + { + return 0; + } + + PyObjectHandle future = createFuture(op, asyncResultObj.get()); + if(!future.get()) + { + return 0; + } + d->setFuture(future.get()); + return future.release(); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* connectionBeginFlushBatchRequests(ConnectionObject* self, PyObject* args, PyObject* kwds) { assert(self->connection); @@ -821,6 +865,8 @@ static PyMethodDef ConnectionMethods[] = PyDoc_STR(STRCAST("getAdapter() -> Ice.ObjectAdapter")) }, { STRCAST("flushBatchRequests"), reinterpret_cast<PyCFunction>(connectionFlushBatchRequests), METH_NOARGS, PyDoc_STR(STRCAST("flushBatchRequests() -> None")) }, + { STRCAST("flushBatchRequestsAsync"), reinterpret_cast<PyCFunction>(connectionFlushBatchRequestsAsync), + METH_NOARGS, PyDoc_STR(STRCAST("flushBatchRequestsAsync() -> Ice.Future")) }, { STRCAST("begin_flushBatchRequests"), reinterpret_cast<PyCFunction>(connectionBeginFlushBatchRequests), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_flushBatchRequests([_ex][, _sent]) -> Ice.AsyncResult")) }, { STRCAST("end_flushBatchRequests"), reinterpret_cast<PyCFunction>(connectionEndFlushBatchRequests), METH_VARARGS, diff --git a/python/modules/IcePy/ObjectAdapter.cpp b/python/modules/IcePy/ObjectAdapter.cpp index a51cca80c93..d2718b1ca50 100644 --- a/python/modules/IcePy/ObjectAdapter.cpp +++ b/python/modules/IcePy/ObjectAdapter.cpp @@ -181,7 +181,7 @@ IcePy::ServantLocatorWrapper::locate(const Ice::Current& current, Ice::LocalObje { if(PyTuple_GET_SIZE(res.get()) > 2) { - PyErr_Warn(PyExc_RuntimeWarning, STRCAST("invalid return value for ServantLocator::locate")); + PyErr_WarnEx(PyExc_RuntimeWarning, STRCAST("invalid return value for ServantLocator::locate"), 1); return 0; } servantObj = PyTuple_GET_ITEM(res.get(), 0); @@ -200,7 +200,7 @@ IcePy::ServantLocatorWrapper::locate(const Ice::Current& current, Ice::LocalObje // if(!PyObject_IsInstance(servantObj, _objectType)) { - PyErr_Warn(PyExc_RuntimeWarning, STRCAST("return value of ServantLocator::locate is not an Ice object")); + PyErr_WarnEx(PyExc_RuntimeWarning, STRCAST("return value of ServantLocator::locate is not an Ice object"), 1); return 0; } diff --git a/python/modules/IcePy/Operation.cpp b/python/modules/IcePy/Operation.cpp index 3b62b8aef59..6c32d861806 100644 --- a/python/modules/IcePy/Operation.cpp +++ b/python/modules/IcePy/Operation.cpp @@ -27,6 +27,7 @@ #include <Ice/AsyncResult.h> #include <Ice/Properties.h> #include <Ice/Proxy.h> +#include <IceUtil/Time.h> #include <Slice/PythonUtil.h> using namespace std; @@ -104,50 +105,44 @@ public: protected: - Ice::ObjectPrx _prx; -}; -typedef IceUtil::Handle<Invocation> InvocationPtr; - -// -// TypedInvocation uses the information in the given Operation to validate, marshal, and unmarshal -// parameters and exceptions. -// -class TypedInvocation : virtual public Invocation -{ -public: + // + // Helpers for typed invocations. + // - TypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); + enum MappingType { SyncMapping, AsyncMapping, NewAsyncMapping }; -protected: + bool prepareRequest(const OperationPtr&, PyObject*, MappingType, Ice::OutputStream*, + pair<const Ice::Byte*, const Ice::Byte*>&); + PyObject* unmarshalResults(const OperationPtr&, const pair<const Ice::Byte*, const Ice::Byte*>&); + PyObject* unmarshalException(const OperationPtr&, const pair<const Ice::Byte*, const Ice::Byte*>&); + bool validateException(const OperationPtr&, PyObject*) const; + void checkTwowayOnly(const OperationPtr&, const Ice::ObjectPrx&) const; - OperationPtr _op; + Ice::ObjectPrx _prx; Ice::CommunicatorPtr _communicator; - - enum MappingType { SyncMapping, AsyncMapping, OldAsyncMapping }; - - bool prepareRequest(PyObject*, MappingType, Ice::OutputStream*, pair<const Ice::Byte*, const Ice::Byte*>&); - PyObject* unmarshalResults(const pair<const Ice::Byte*, const Ice::Byte*>&); - PyObject* unmarshalException(const pair<const Ice::Byte*, const Ice::Byte*>&); - bool validateException(PyObject*) const; - void checkTwowayOnly(const Ice::ObjectPrx&) const; }; +typedef IceUtil::Handle<Invocation> InvocationPtr; // // Synchronous typed invocation. // -class SyncTypedInvocation : virtual public TypedInvocation +class SyncTypedInvocation : public Invocation { public: SyncTypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); virtual PyObject* invoke(PyObject*, PyObject* = 0); + +private: + + OperationPtr _op; }; // // Asynchronous typed invocation. // -class AsyncTypedInvocation : virtual public TypedInvocation +class AsyncTypedInvocation : public Invocation { public: @@ -163,10 +158,11 @@ public: void exception(const Ice::Exception&); void sent(bool); -protected: +private: void checkAsyncTwowayOnly(const Ice::ObjectPrx&) const; + OperationPtr _op; PyObject* _pyProxy; PyObject* _response; PyObject* _ex; @@ -175,14 +171,14 @@ protected: typedef IceUtil::Handle<AsyncTypedInvocation> AsyncTypedInvocationPtr; // -// Old-style asynchronous typed invocation. +// Asynchronous invocation with futures. // -class OldAsyncTypedInvocation : virtual public TypedInvocation +class NewAsyncInvocation : public Invocation { public: - OldAsyncTypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); - ~OldAsyncTypedInvocation(); + NewAsyncInvocation(const Ice::ObjectPrx&, PyObject*, const string&); + ~NewAsyncInvocation(); virtual PyObject* invoke(PyObject*, PyObject* = 0); @@ -192,13 +188,47 @@ public: protected: - PyObject* _callback; + virtual Ice::AsyncResultPtr handleInvoke(PyObject*, PyObject*) = 0; + virtual void handleResponse(PyObject*, bool, const pair<const Ice::Byte*, const Ice::Byte*>&) = 0; + + PyObject* _pyProxy; + string _operation; + bool _twoway; + bool _sent; + bool _sentSynchronously; + bool _done; + PyObject* _future; + bool _ok; + vector<Ice::Byte> _results; + PyObject* _exception; +}; +typedef IceUtil::Handle<NewAsyncInvocation> NewAsyncInvocationPtr; + +// +// New-style asynchronous typed invocation. +// +class NewAsyncTypedInvocation : public NewAsyncInvocation +{ +public: + + NewAsyncTypedInvocation(const Ice::ObjectPrx&, PyObject*, const OperationPtr&); + +protected: + + virtual Ice::AsyncResultPtr handleInvoke(PyObject*, PyObject*); + virtual void handleResponse(PyObject*, bool, const pair<const Ice::Byte*, const Ice::Byte*>&); + +private: + + void checkAsyncTwowayOnly(const Ice::ObjectPrx&) const; + + OperationPtr _op; }; // // Synchronous blobject invocation. // -class SyncBlobjectInvocation : virtual public Invocation +class SyncBlobjectInvocation : public Invocation { public: @@ -210,7 +240,7 @@ public: // // Asynchronous blobject invocation. // -class AsyncBlobjectInvocation : virtual public Invocation +class AsyncBlobjectInvocation : public Invocation { public: @@ -235,80 +265,83 @@ protected: typedef IceUtil::Handle<AsyncBlobjectInvocation> AsyncBlobjectInvocationPtr; // -// Old-style asynchronous blobject invocation. +// New-style asynchronous blobject invocation. // -class OldAsyncBlobjectInvocation : virtual public Invocation +class NewAsyncBlobjectInvocation : public NewAsyncInvocation { public: - OldAsyncBlobjectInvocation(const Ice::ObjectPrx&); - ~OldAsyncBlobjectInvocation(); - - virtual PyObject* invoke(PyObject*, PyObject* = 0); - - void response(bool, const pair<const Ice::Byte*, const Ice::Byte*>&); - void exception(const Ice::Exception&); - void sent(bool); + NewAsyncBlobjectInvocation(const Ice::ObjectPrx&, PyObject*); protected: + virtual Ice::AsyncResultPtr handleInvoke(PyObject*, PyObject*); + virtual void handleResponse(PyObject*, bool, const pair<const Ice::Byte*, const Ice::Byte*>&); + string _op; - PyObject* _callback; }; // // The base class for server-side upcalls. // -class Upcall : virtual public IceUtil::Shared +class Upcall : public IceUtil::Shared { public: virtual void dispatch(PyObject*, const pair<const Ice::Byte*, const Ice::Byte*>&, const Ice::Current&) = 0; - virtual void response(PyObject*, const Ice::EncodingVersion&) = 0; - virtual void exception(PyException&, const Ice::EncodingVersion&) = 0; + virtual void response(PyObject*) = 0; + virtual void exception(PyException&) = 0; + virtual void exception(const Ice::Exception&) = 0; + +protected: + + void dispatchImpl(PyObject*, const string&, PyObject*, const Ice::Current&); }; typedef IceUtil::Handle<Upcall> UpcallPtr; // -// TypedInvocation uses the information in the given Operation to validate, marshal, and unmarshal +// TypedUpcall uses the information in the given Operation to validate, marshal, and unmarshal // parameters and exceptions. // -class TypedUpcall : virtual public Upcall +class TypedUpcall; +typedef IceUtil::Handle<TypedUpcall> TypedUpcallPtr; + +class TypedUpcall : public Upcall { public: TypedUpcall(const OperationPtr&, const Ice::AMD_Object_ice_invokePtr&, const Ice::CommunicatorPtr&); virtual void dispatch(PyObject*, const pair<const Ice::Byte*, const Ice::Byte*>&, const Ice::Current&); - virtual void response(PyObject*, const Ice::EncodingVersion&); - virtual void exception(PyException&, const Ice::EncodingVersion&); + virtual void response(PyObject*); + virtual void exception(PyException&); + virtual void exception(const Ice::Exception&); private: OperationPtr _op; Ice::AMD_Object_ice_invokePtr _callback; Ice::CommunicatorPtr _communicator; - bool _finished; + Ice::EncodingVersion _encoding; }; // // Upcall for blobject servants. // -class BlobjectUpcall : virtual public Upcall +class BlobjectUpcall : public Upcall { public: - BlobjectUpcall(bool, const Ice::AMD_Object_ice_invokePtr&); + BlobjectUpcall(const Ice::AMD_Object_ice_invokePtr&); virtual void dispatch(PyObject*, const pair<const Ice::Byte*, const Ice::Byte*>&, const Ice::Current&); - virtual void response(PyObject*, const Ice::EncodingVersion&); - virtual void exception(PyException&, const Ice::EncodingVersion&); + virtual void response(PyObject*); + virtual void exception(PyException&); + virtual void exception(const Ice::Exception&); private: - bool _amd; Ice::AMD_Object_ice_invokePtr _callback; - bool _finished; }; // @@ -356,11 +389,19 @@ struct OperationObject OperationPtr* op; }; -struct AMDCallbackObject +struct DoneCallbackObject +{ + PyObject_HEAD + UpcallPtr* upcall; +#if PY_VERSION_HEX >= 0x03050000 + PyObject* coroutine; +#endif +}; + +struct DispatchCallbackObject { PyObject_HEAD UpcallPtr* upcall; - Ice::EncodingVersion encoding; }; struct AsyncResultObject @@ -374,7 +415,6 @@ struct AsyncResultObject }; extern PyTypeObject OperationType; -extern PyTypeObject AMDCallbackType; class UserExceptionFactory : public Ice::UserExceptionFactory { @@ -424,8 +464,7 @@ handleException() void callException(PyObject* method, PyObject* ex) { - PyObjectHandle args = Py_BuildValue(STRCAST("(O)"), ex); - PyObjectHandle tmp = PyObject_Call(method, args.get(), 0); + PyObjectHandle tmp = callMethod(method, ex); if(PyErr_Occurred()) { handleException(); // Callback raised an exception. @@ -441,61 +480,20 @@ callException(PyObject* method, const Ice::Exception& ex) } void -callException(PyObject* callback, const string& op, const string& method, PyObject* ex) -{ - if(!PyObject_HasAttrString(callback, STRCAST(method.c_str()))) - { - ostringstream ostr; - ostr << "AMI callback object for operation `" << op << "' does not define " << method << "()"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - } - else - { - PyObjectHandle m = PyObject_GetAttrString(callback, STRCAST(method.c_str())); - assert(m.get()); - callException(m.get(), ex); - } -} - -void -callException(PyObject* callback, const string& op, const string& method, const Ice::Exception& ex) -{ - PyObjectHandle exh = convertException(ex); - assert(exh.get()); - callException(callback, op, method, exh.get()); -} - -void callSent(PyObject* method, bool sentSynchronously, bool passArg) { - PyObjectHandle args; + PyObject* arg = 0; if(passArg) { - args = Py_BuildValue(STRCAST("(O)"), sentSynchronously ? getTrue() : getFalse()); + arg = sentSynchronously ? getTrue() : getFalse(); } - else - { - args = PyTuple_New(0); - } - PyObjectHandle tmp = PyObject_Call(method, args.get(), 0); + PyObjectHandle tmp = callMethod(method, arg); if(PyErr_Occurred()) { handleException(); // Callback raised an exception. } } -void -callSent(PyObject* callback, const string& method, bool sentSynchronously, bool passArg) -{ - if(PyObject_HasAttrString(callback, STRCAST(method.c_str()))) - { - PyObjectHandle m = PyObject_GetAttrString(callback, STRCAST(method.c_str())); - assert(m.get()); - callSent(m.get(), sentSynchronously, passArg); - } -} - } #ifdef WIN32 @@ -581,17 +579,15 @@ extern "C" static PyObject* operationInvokeAsync(OperationObject* self, PyObject* args) { - PyObject* pyProxy; + PyObject* proxy; PyObject* opArgs; - if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &pyProxy, &PyTuple_Type, &opArgs)) + if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &proxy, &PyTuple_Type, &opArgs)) { return 0; } - Ice::ObjectPrx prx = getProxy(pyProxy); - assert(self->op); - - InvocationPtr i = new OldAsyncTypedInvocation(prx, *self->op); + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new NewAsyncTypedInvocation(p, proxy, *self->op); return i->invoke(opArgs); } @@ -654,26 +650,28 @@ operationDeprecate(OperationObject* self, PyObject* args) assert(self->op); (*self->op)->deprecate(msg); - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } // -// AMDCallback operations +// DoneCallback operations // #ifdef WIN32 extern "C" #endif -static AMDCallbackObject* -amdCallbackNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) +static DoneCallbackObject* +doneCallbackNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) { - AMDCallbackObject* self = reinterpret_cast<AMDCallbackObject*>(type->tp_alloc(type, 0)); + DoneCallbackObject* self = reinterpret_cast<DoneCallbackObject*>(type->tp_alloc(type, 0)); if(!self) { return 0; } self->upcall = 0; +#if PY_VERSION_HEX >= 0x03050000 + self->coroutine = 0; +#endif return self; } @@ -681,9 +679,12 @@ amdCallbackNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) extern "C" #endif static void -amdCallbackDealloc(AMDCallbackObject* self) +doneCallbackDealloc(DoneCallbackObject* self) { delete self->upcall; +#if PY_VERSION_HEX >= 0x03050000 + Py_XDECREF(self->coroutine); +#endif Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); } @@ -691,12 +692,32 @@ amdCallbackDealloc(AMDCallbackObject* self) extern "C" #endif static PyObject* -amdCallbackIceResponse(AMDCallbackObject* self, PyObject* args) +doneCallbackInvoke(DoneCallbackObject* self, PyObject* args) { + PyObject* future = 0; + if(!PyArg_ParseTuple(args, STRCAST("O"), &future)) + { + return 0; + } + try { assert(self->upcall); - (*self->upcall)->response(args, self->encoding); + + PyObjectHandle resultMethod = PyObject_GetAttrString(future, STRCAST("result")); + assert(resultMethod.get()); + PyObjectHandle empty = PyTuple_New(0); + PyObjectHandle result = PyObject_Call(resultMethod.get(), empty.get(), 0); + + if(PyErr_Occurred()) + { + PyException ex; + (*self->upcall)->exception(ex); + } + else + { + (*self->upcall)->response(result.get()); + } } catch(...) { @@ -706,33 +727,83 @@ amdCallbackIceResponse(AMDCallbackObject* self, PyObject* args) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); +} + +// +// DispatchCallbackObject operations +// + +#ifdef WIN32 +extern "C" +#endif +static DispatchCallbackObject* +dispatchCallbackNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) +{ + DispatchCallbackObject* self = reinterpret_cast<DispatchCallbackObject*>(type->tp_alloc(type, 0)); + if(!self) + { + return 0; + } + self->upcall = 0; + return self; +} + +#ifdef WIN32 +extern "C" +#endif +static void +dispatchCallbackDealloc(DispatchCallbackObject* self) +{ + delete self->upcall; + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); } #ifdef WIN32 extern "C" #endif static PyObject* -amdCallbackIceException(AMDCallbackObject* self, PyObject* args) +dispatchCallbackResponse(DispatchCallbackObject* self, PyObject* args) { - PyObject* ex; - if(!PyArg_ParseTuple(args, STRCAST("O"), &ex)) + PyObject* result = 0; + if(!PyArg_ParseTuple(args, STRCAST("O"), &result)) { return 0; } - if(!PyObject_IsInstance(ex, PyExc_Exception)) + try + { + assert(self->upcall); + (*self->upcall)->response(result); + } + catch(...) + { + // + // No exceptions should propagate to Python. + // + assert(false); + } + + return incRef(Py_None); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +dispatchCallbackException(DispatchCallbackObject* self, PyObject* args) +{ + PyObject* ex = 0; + if(!PyArg_ParseTuple(args, STRCAST("O"), &ex)) { - PyErr_Format(PyExc_TypeError, "ice_exception argument is not an exception"); return 0; } try { assert(self->upcall); - PyException pye(ex); // No traceback information available. - (*self->upcall)->exception(pye, self->encoding); + PyException pyex(ex); + (*self->upcall)->exception(pyex); } catch(...) { @@ -742,8 +813,7 @@ amdCallbackIceException(AMDCallbackObject* self, PyObject* args) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } // @@ -791,12 +861,10 @@ asyncResultGetCommunicator(AsyncResultObject* self) { if(self->communicator) { - Py_INCREF(self->communicator); - return self->communicator; + return incRef(self->communicator); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -814,8 +882,7 @@ asyncResultCancel(AsyncResultObject* self) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -826,12 +893,10 @@ asyncResultGetConnection(AsyncResultObject* self) { if(self->connection) { - Py_INCREF(self->connection); - return self->connection; + return incRef(self->connection); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -842,12 +907,10 @@ asyncResultGetProxy(AsyncResultObject* self) { if(self->proxy) { - Py_INCREF(self->proxy); - return self->proxy; + return incRef(self->proxy); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -888,8 +951,7 @@ asyncResultWaitForCompleted(AsyncResultObject* self) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -930,8 +992,7 @@ asyncResultWaitForSent(AsyncResultObject* self) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -955,8 +1016,7 @@ asyncResultThrowLocalException(AsyncResultObject* self) assert(false); } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } #ifdef WIN32 @@ -1017,6 +1077,72 @@ asyncResultGetOperation(AsyncResultObject* self) return createString(op); } +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultCallLater(AsyncResultObject* self, PyObject* args) +{ + PyObject* callback; + if(!PyArg_ParseTuple(args, STRCAST("O"), &callback)) + { + return 0; + } + + if(!PyCallable_Check(callback)) + { + PyErr_Format(PyExc_ValueError, STRCAST("invalid argument passed to callLater")); + return 0; + } + + class CallbackI : public Ice::AsyncResult::Callback + { + public: + + CallbackI(PyObject* callback) : + _callback(incRef(callback)) + { + } + + ~CallbackI() + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_callback); + } + + virtual void run() + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + PyObjectHandle args = PyTuple_New(0); + assert(args.get()); + PyObjectHandle tmp = PyObject_Call(_callback, args.get(), 0); + PyErr_Clear(); + } + + private: + + PyObject* _callback; + }; + + try + { + (*self->result)->scheduleCallback(new CallbackI(callback)); + } + catch(const Ice::CommunicatorDestroyedException& ex) + { + setPythonException(ex); + return 0; + } + catch(...) + { + assert(false); + } + + return incRef(Py_None); +} + // // ParamInfo implementation. // @@ -1025,8 +1151,7 @@ IcePy::ParamInfo::unmarshaled(PyObject* val, PyObject* target, void* closure) { assert(PyTuple_Check(target)); Py_ssize_t i = reinterpret_cast<Py_ssize_t>(closure); - PyTuple_SET_ITEM(target, i, val); - Py_INCREF(val); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(target, i, incRef(val)); // PyTuple_SET_ITEM steals a reference. } // @@ -1055,14 +1180,7 @@ IcePy::Operation::Operation(const char* n, PyObject* m, PyObject* sm, int amdFla // amd // amd = amdFlag ? true : false; - if(amd) - { - dispatchName = name + "_async"; - } - else - { - dispatchName = fixIdent(name); - } + dispatchName = fixIdent(name); // Use the same dispatch name regardless of AMD. // // format @@ -1252,11 +1370,18 @@ static PyMethodDef OperationMethods[] = { 0, 0 } /* sentinel */ }; -static PyMethodDef AMDCallbackMethods[] = +static PyMethodDef DoneCallbackMethods[] = +{ + { STRCAST("invoke"), reinterpret_cast<PyCFunction>(doneCallbackInvoke), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { 0, 0 } /* sentinel */ +}; + +static PyMethodDef DispatchCallbackMethods[] = { - { STRCAST("ice_response"), reinterpret_cast<PyCFunction>(amdCallbackIceResponse), METH_VARARGS, + { STRCAST("response"), reinterpret_cast<PyCFunction>(dispatchCallbackResponse), METH_VARARGS, PyDoc_STR(STRCAST("internal function")) }, - { STRCAST("ice_exception"), reinterpret_cast<PyCFunction>(amdCallbackIceException), METH_VARARGS, + { STRCAST("exception"), reinterpret_cast<PyCFunction>(dispatchCallbackException), METH_VARARGS, PyDoc_STR(STRCAST("internal function")) }, { 0, 0 } /* sentinel */ }; @@ -1285,6 +1410,8 @@ static PyMethodDef AsyncResultMethods[] = PyDoc_STR(STRCAST("returns true if the request was sent synchronously")) }, { STRCAST("getOperation"), reinterpret_cast<PyCFunction>(asyncResultGetOperation), METH_NOARGS, PyDoc_STR(STRCAST("returns the name of the operation")) }, + { STRCAST("callLater"), reinterpret_cast<PyCFunction>(asyncResultCallLater), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, { 0, 0 } /* sentinel */ }; @@ -1338,16 +1465,16 @@ PyTypeObject OperationType = 0, /* tp_is_gc */ }; -PyTypeObject AMDCallbackType = +static PyTypeObject DoneCallbackType = { /* The ob_type field must be initialized in the module init function * to be portable to Windows without using C++. */ PyVarObject_HEAD_INIT(0, 0) - STRCAST("IcePy.AMDCallback"), /* tp_name */ - sizeof(AMDCallbackObject), /* tp_basicsize */ + STRCAST("IcePy.DoneCallback"), /* tp_name */ + sizeof(DoneCallbackObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ - reinterpret_cast<destructor>(amdCallbackDealloc), /* tp_dealloc */ + reinterpret_cast<destructor>(doneCallbackDealloc), /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -1370,7 +1497,7 @@ PyTypeObject AMDCallbackType = 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - AMDCallbackMethods, /* tp_methods */ + DoneCallbackMethods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ @@ -1380,7 +1507,54 @@ PyTypeObject AMDCallbackType = 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ - reinterpret_cast<newfunc>(amdCallbackNew), /* tp_new */ + reinterpret_cast<newfunc>(doneCallbackNew), /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +static PyTypeObject DispatchCallbackType = +{ + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(0, 0) + STRCAST("IcePy.Dispatch"), /* tp_name */ + sizeof(DispatchCallbackObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + reinterpret_cast<destructor>(dispatchCallbackDealloc), /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + DispatchCallbackMethods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + reinterpret_cast<newfunc>(dispatchCallbackNew), /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ }; @@ -1447,12 +1621,22 @@ IcePy::initOperation(PyObject* module) return false; } - if(PyType_Ready(&AMDCallbackType) < 0) + if(PyType_Ready(&DoneCallbackType) < 0) { return false; } - PyTypeObject* cbType = &AMDCallbackType; // Necessary to prevent GCC's strict-alias warnings. - if(PyModule_AddObject(module, STRCAST("AMDCallback"), reinterpret_cast<PyObject*>(cbType)) < 0) + PyTypeObject* cbType = &DoneCallbackType; // Necessary to prevent GCC's strict-alias warnings. + if(PyModule_AddObject(module, STRCAST("DoneCallback"), reinterpret_cast<PyObject*>(cbType)) < 0) + { + return false; + } + + if(PyType_Ready(&DispatchCallbackType) < 0) + { + return false; + } + PyTypeObject* dispatchType = &DispatchCallbackType; // Necessary to prevent GCC's strict-alias warnings. + if(PyModule_AddObject(module, STRCAST("DispatchCallback"), reinterpret_cast<PyObject*>(dispatchType)) < 0) { return false; } @@ -1474,21 +1658,13 @@ IcePy::initOperation(PyObject* module) // Invocation // IcePy::Invocation::Invocation(const Ice::ObjectPrx& prx) : - _prx(prx) -{ -} - -// -// TypedInvocation -// -IcePy::TypedInvocation::TypedInvocation(const Ice::ObjectPrx& prx, const OperationPtr& op) : - Invocation(prx), _op(op), _communicator(prx->ice_getCommunicator()) + _prx(prx), _communicator(prx->ice_getCommunicator()) { } bool -IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice::OutputStream* os, - pair<const Ice::Byte*, const Ice::Byte*>& params) +IcePy::Invocation::prepareRequest(const OperationPtr& op, PyObject* args, MappingType mapping, Ice::OutputStream* os, + pair<const Ice::Byte*, const Ice::Byte*>& params) { assert(PyTuple_Check(args)); params.first = params.second = static_cast<const Ice::Byte*>(0); @@ -1497,35 +1673,35 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: // Validate the number of arguments. // Py_ssize_t argc = PyTuple_GET_SIZE(args); - Py_ssize_t paramCount = static_cast<Py_ssize_t>(_op->inParams.size()); + Py_ssize_t paramCount = static_cast<Py_ssize_t>(op->inParams.size()); if(argc != paramCount) { string opName; - if(mapping == OldAsyncMapping) + if(mapping == NewAsyncMapping) { - opName = _op->name + "_async"; + opName = op->name + "Async"; } else if(mapping == AsyncMapping) { - opName = "begin_" + _op->name; + opName = "begin_" + op->name; } else { - opName = fixIdent(_op->name); + opName = fixIdent(op->name); } PyErr_Format(PyExc_RuntimeError, STRCAST("%s expects %d in parameters"), opName.c_str(), static_cast<int>(paramCount)); return false; } - if(!_op->inParams.empty()) + if(!op->inParams.empty()) { try { // // Marshal the in parameters. // - os->startEncapsulation(_prx->ice_getEncodingVersion(), _op->format); + os->startEncapsulation(_prx->ice_getEncodingVersion(), op->format); ObjectMap objectMap; ParamInfoList::iterator p; @@ -1533,28 +1709,27 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: // // Validate the supplied arguments. // - for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p) + for(p = op->inParams.begin(); p != op->inParams.end(); ++p) { ParamInfoPtr info = *p; PyObject* arg = PyTuple_GET_ITEM(args, info->pos); if((!info->optional || arg != Unset) && !info->type->validate(arg)) { string name; - if(mapping == OldAsyncMapping) + if(mapping == NewAsyncMapping) { - name = _op->name + "_async"; + name = op->name + "Async"; } else if(mapping == AsyncMapping) { - name = "begin_" + _op->name; + name = "begin_" + op->name; } else { - name = fixIdent(_op->name); + name = fixIdent(op->name); } PyErr_Format(PyExc_ValueError, STRCAST("invalid value for argument %d in operation `%s'"), - mapping == OldAsyncMapping ? info->pos + 2 : info->pos + 1, - const_cast<char*>(name.c_str())); + info->pos + 1, const_cast<char*>(name.c_str())); return false; } } @@ -1562,7 +1737,7 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: // // Marshal the required parameters. // - for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p) + for(p = op->inParams.begin(); p != op->inParams.end(); ++p) { ParamInfoPtr info = *p; if(!info->optional) @@ -1575,7 +1750,7 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: // // Marshal the optional parameters. // - for(p = _op->optionalInParams.begin(); p != _op->optionalInParams.end(); ++p) + for(p = op->optionalInParams.begin(); p != op->optionalInParams.end(); ++p) { ParamInfoPtr info = *p; PyObject* arg = PyTuple_GET_ITEM(args, info->pos); @@ -1585,7 +1760,7 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: } } - if(_op->sendsClasses) + if(op->sendsClasses) { os->writePendingValues(); } @@ -1609,10 +1784,10 @@ IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice: } PyObject* -IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice::Byte*>& bytes) +IcePy::Invocation::unmarshalResults(const OperationPtr& op, const pair<const Ice::Byte*, const Ice::Byte*>& bytes) { - Py_ssize_t numResults = static_cast<Py_ssize_t>(_op->outParams.size()); - if(_op->returnType) + Py_ssize_t numResults = static_cast<Py_ssize_t>(op->outParams.size()); + if(op->returnType) { numResults++; } @@ -1637,7 +1812,7 @@ IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice: // // Unmarshal the required out parameters. // - for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) + for(p = op->outParams.begin(); p != op->outParams.end(); ++p) { ParamInfoPtr info = *p; if(!info->optional) @@ -1650,17 +1825,17 @@ IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice: // // Unmarshal the required return value, if any. // - if(_op->returnType && !_op->returnType->optional) + if(op->returnType && !op->returnType->optional) { - assert(_op->returnType->pos == 0); - void* closure = reinterpret_cast<void*>(static_cast<Py_ssize_t>(_op->returnType->pos)); - _op->returnType->type->unmarshal(&is, _op->returnType, results.get(), closure, false, &_op->metaData); + assert(op->returnType->pos == 0); + void* closure = reinterpret_cast<void*>(static_cast<Py_ssize_t>(op->returnType->pos)); + op->returnType->type->unmarshal(&is, op->returnType, results.get(), closure, false, &op->metaData); } // // Unmarshal the optional results. This includes an optional return value. // - for(p = _op->optionalOutParams.begin(); p != _op->optionalOutParams.end(); ++p) + for(p = op->optionalOutParams.begin(); p != op->optionalOutParams.end(); ++p) { ParamInfoPtr info = *p; if(is.readOptional(info->tag, info->type->optionalFormat())) @@ -1670,15 +1845,11 @@ IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice: } else { - if(PyTuple_SET_ITEM(results.get(), info->pos, Unset) < 0) - { - return 0; - } - Py_INCREF(Unset); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(results.get(), info->pos, incRef(Unset)); // PyTuple_SET_ITEM steals a reference. } } - if(_op->returnsClasses) + if(op->returnsClasses) { is.readPendingValues(); } @@ -1692,7 +1863,7 @@ IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice: } PyObject* -IcePy::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ice::Byte*>& bytes) +IcePy::Invocation::unmarshalException(const OperationPtr& op, const pair<const Ice::Byte*, const Ice::Byte*>& bytes) { Ice::InputStream is(_communicator, bytes); @@ -1717,7 +1888,7 @@ IcePy::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ic PyObject* ex = r.getException(); - if(validateException(ex)) + if(validateException(op, ex)) { util.updateSlicedData(); @@ -1727,8 +1898,7 @@ IcePy::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ic StreamUtil::setSlicedDataMember(ex, slicedData); } - Py_INCREF(ex); - return ex; + return incRef(ex); } else { @@ -1758,9 +1928,9 @@ IcePy::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ic } bool -IcePy::TypedInvocation::validateException(PyObject* ex) const +IcePy::Invocation::validateException(const OperationPtr& op, PyObject* ex) const { - for(ExceptionInfoList::const_iterator p = _op->exceptions.begin(); p != _op->exceptions.end(); ++p) + for(ExceptionInfoList::const_iterator p = op->exceptions.begin(); p != op->exceptions.end(); ++p) { if(PyObject_IsInstance(ex, (*p)->pythonType.get())) { @@ -1772,12 +1942,12 @@ IcePy::TypedInvocation::validateException(PyObject* ex) const } void -IcePy::TypedInvocation::checkTwowayOnly(const Ice::ObjectPrx& proxy) const +IcePy::Invocation::checkTwowayOnly(const OperationPtr& op, const Ice::ObjectPrx& proxy) const { - if((_op->returnType != 0 || !_op->outParams.empty() || !_op->exceptions.empty()) && !proxy->ice_isTwoway()) + if((op->returnType != 0 || !op->outParams.empty() || !op->exceptions.empty()) && !proxy->ice_isTwoway()) { Ice::TwowayOnlyException ex(__FILE__, __LINE__); - ex.operation = _op->name; + ex.operation = op->name; throw ex; } } @@ -1786,7 +1956,7 @@ IcePy::TypedInvocation::checkTwowayOnly(const Ice::ObjectPrx& proxy) const // SyncTypedInvocation // IcePy::SyncTypedInvocation::SyncTypedInvocation(const Ice::ObjectPrx& prx, const OperationPtr& op) : - Invocation(prx), TypedInvocation(prx, op) + Invocation(prx), _op(op) { } @@ -1804,14 +1974,14 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) // Ice::OutputStream os(_communicator); pair<const Ice::Byte*, const Ice::Byte*> params; - if(!prepareRequest(pyparams, SyncMapping, &os, params)) + if(!prepareRequest(_op, pyparams, SyncMapping, &os, params)) { return 0; } try { - checkTwowayOnly(_prx); + checkTwowayOnly(_op, _prx); // // Invoke the operation. @@ -1861,7 +2031,7 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) rb.first = &result[0]; rb.second = &result[0] + result.size(); } - PyObjectHandle ex = unmarshalException(rb); + PyObjectHandle ex = unmarshalException(_op, rb); // // Set the Python exception. @@ -1882,7 +2052,7 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) rb.first = &result[0]; rb.second = &result[0] + result.size(); } - PyObjectHandle results = unmarshalResults(rb); + PyObjectHandle results = unmarshalResults(_op, rb); if(!results.get()) { return 0; @@ -1901,8 +2071,7 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) } else { - Py_INCREF(ret); - return ret; + return incRef(ret); } } } @@ -1919,8 +2088,7 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) return 0; } - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } // @@ -1928,7 +2096,7 @@ IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) // IcePy::AsyncTypedInvocation::AsyncTypedInvocation(const Ice::ObjectPrx& prx, PyObject* pyProxy, const OperationPtr& op) : - Invocation(prx), TypedInvocation(prx, op), _pyProxy(pyProxy), _response(0), _ex(0), _sent(0) + Invocation(prx), _op(op), _pyProxy(pyProxy), _response(0), _ex(0), _sent(0) { Py_INCREF(_pyProxy); } @@ -1956,8 +2124,7 @@ IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) callable = PyTuple_GET_ITEM(args, 1); if(PyCallable_Check(callable)) { - _response = callable; - Py_INCREF(_response); + _response = incRef(callable); } else if(callable != Py_None) { @@ -1968,8 +2135,7 @@ IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) callable = PyTuple_GET_ITEM(args, 2); if(PyCallable_Check(callable)) { - _ex = callable; - Py_INCREF(_ex); + _ex = incRef(callable); } else if(callable != Py_None) { @@ -1980,8 +2146,7 @@ IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) callable = PyTuple_GET_ITEM(args, 3); if(PyCallable_Check(callable)) { - _sent = callable; - Py_INCREF(_sent); + _sent = incRef(callable); } else if(callable != Py_None) { @@ -2008,7 +2173,7 @@ IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) // Ice::OutputStream os(_communicator); pair<const Ice::Byte*, const Ice::Byte*> params; - if(!prepareRequest(pyparams, AsyncMapping, &os, params)) + if(!prepareRequest(_op, pyparams, AsyncMapping, &os, params)) { return 0; } @@ -2092,8 +2257,7 @@ IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) } obj->result = new Ice::AsyncResultPtr(result); obj->invocation = new InvocationPtr(this); - obj->proxy = _pyProxy; - Py_INCREF(obj->proxy); + obj->proxy = incRef(_pyProxy); obj->communicator = getCommunicatorWrapper(_communicator); return reinterpret_cast<PyObject*>(obj); } @@ -2122,7 +2286,7 @@ IcePy::AsyncTypedInvocation::end(const Ice::ObjectPrx& proxy, const OperationPtr // // Unmarshal the results. // - PyObjectHandle args = unmarshalResults(results); + PyObjectHandle args = unmarshalResults(_op, results); if(args.get()) { // @@ -2132,14 +2296,12 @@ IcePy::AsyncTypedInvocation::end(const Ice::ObjectPrx& proxy, const OperationPtr assert(PyTuple_Check(args.get())); if(PyTuple_GET_SIZE(args.get()) == 0) { - Py_INCREF(Py_None); - return Py_None; + return incRef(Py_None); } else if(PyTuple_GET_SIZE(args.get()) == 1) { PyObject* res = PyTuple_GET_ITEM(args.get(), 0); - Py_INCREF(res); - return res; + return incRef(res); } else { @@ -2149,7 +2311,7 @@ IcePy::AsyncTypedInvocation::end(const Ice::ObjectPrx& proxy, const OperationPtr } else { - PyObjectHandle ex = unmarshalException(results); + PyObjectHandle ex = unmarshalException(_op, results); setPythonException(ex.get()); } } @@ -2193,7 +2355,7 @@ IcePy::AsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, cons PyObjectHandle args; try { - args = unmarshalResults(results); + args = unmarshalResults(_op, results); if(!args.get()) { assert(PyErr_Occurred()); @@ -2218,7 +2380,7 @@ IcePy::AsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, cons else { assert(_ex); - PyObjectHandle ex = unmarshalException(results); + PyObjectHandle ex = unmarshalException(_op, results); callException(_ex, ex.get()); } } @@ -2263,114 +2425,344 @@ IcePy::AsyncTypedInvocation::checkAsyncTwowayOnly(const Ice::ObjectPrx& proxy) c } // -// OldAsyncTypedInvocation +// NewAsyncInvocation // -IcePy::OldAsyncTypedInvocation::OldAsyncTypedInvocation(const Ice::ObjectPrx& prx, const OperationPtr& op) - : Invocation(prx), TypedInvocation(prx, op), _callback(0) +IcePy::NewAsyncInvocation::NewAsyncInvocation(const Ice::ObjectPrx& prx, PyObject* pyProxy, const string& operation) + : Invocation(prx), _pyProxy(pyProxy), _operation(operation), _twoway(prx->ice_isTwoway()), _sent(false), + _sentSynchronously(false), _done(false), _future(0), _ok(false), _exception(0) { + Py_INCREF(_pyProxy); } -IcePy::OldAsyncTypedInvocation::~OldAsyncTypedInvocation() +IcePy::NewAsyncInvocation::~NewAsyncInvocation() { AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - Py_XDECREF(_callback); + Py_DECREF(_pyProxy); + Py_XDECREF(_future); + Py_XDECREF(_exception); } PyObject* -IcePy::OldAsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) +IcePy::NewAsyncInvocation::invoke(PyObject* args, PyObject* kwds) { - assert(PyTuple_Check(args)); - assert(PyTuple_GET_SIZE(args) == 3); // Format is (callback, (params...), context|None) - _callback = PyTuple_GET_ITEM(args, 0); - Py_INCREF(_callback); - PyObject* pyparams = PyTuple_GET_ITEM(args, 1); - assert(PyTuple_Check(pyparams)); - PyObject* pyctx = PyTuple_GET_ITEM(args, 2); - // - // Marshal the input parameters to a byte sequence. + // Called from Python code, so the GIL is already acquired. // - Ice::OutputStream os(_communicator); - pair<const Ice::Byte*, const Ice::Byte*> params; - if(!prepareRequest(pyparams, OldAsyncMapping, &os, params)) + + Ice::AsyncResultPtr result; + + try + { + result = handleInvoke(args, kwds); + } + catch(const Ice::CommunicatorDestroyedException& ex) + { + // + // CommunicatorDestroyedException can propagate directly. + // + setPythonException(ex); + return 0; + } + catch(const IceUtil::IllegalArgumentException& ex) { + // + // IllegalArgumentException can propagate directly. + // (Raised by checkAsyncTwowayOnly) + // + PyErr_Format(PyExc_RuntimeError, "%s", STRCAST(ex.reason().c_str())); return 0; } + catch(const Ice::Exception&) + { + // + // No other exceptions should be raised by begin_ice_invoke. + // + assert(false); + } - bool sentSynchronously = false; - try + if(PyErr_Occurred()) { - checkTwowayOnly(_prx); + return 0; + } - Ice::Callback_Object_ice_invokePtr cb = - Ice::newCallback_Object_ice_invoke(this, &OldAsyncTypedInvocation::response, - &OldAsyncTypedInvocation::exception, &OldAsyncTypedInvocation::sent); + assert(result); - Ice::AsyncResultPtr result; + PyObjectHandle communicatorObj = getCommunicatorWrapper(_communicator); + PyObjectHandle asyncResultObj; - // - // Invoke the operation asynchronously. - // - if(pyctx != Py_None) + // + // Pass the AsyncResult object to the future. Note that this creates a circular reference. + // Don't do this for batch invocations because there is no opportunity to break the circular + // reference. + // + if(!_prx->ice_isBatchOneway() && !_prx->ice_isBatchDatagram()) + { + asyncResultObj = createAsyncResult(result, _pyProxy, 0, communicatorObj.get()); + if(!asyncResultObj.get()) { - Ice::Context ctx; + return 0; + } + } + + // + // NOTE: Any time we call into interpreted Python code there's a chance that another thread will be + // allowed to run! + // + + PyObjectHandle future = createFuture(_operation, asyncResultObj.get()); // Calls into Python code. + if(!future.get()) + { + return 0; + } + + // + // Check if any callbacks have been invoked already. + // + + if(_sent) + { + PyObjectHandle tmp = callMethod(future.get(), "set_sent", _sentSynchronously ? getTrue() : getFalse()); + if(PyErr_Occurred()) + { + return 0; + } - if(!PyDict_Check(pyctx)) + if(!_twoway) + { + // + // For a oneway/datagram invocation, we consider it complete when sent. + // + tmp = callMethod(future.get(), "set_result", Py_None); + if(PyErr_Occurred()) { - PyErr_Format(PyExc_ValueError, STRCAST("context argument must be None or a dictionary")); return 0; } + } + } - if(!dictionaryToContext(pyctx, ctx)) + if(_done) + { + if(_exception) + { + PyObjectHandle tmp = callMethod(future.get(), "set_exception", _exception); + if(PyErr_Occurred()) { return 0; } - - AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. - result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params, ctx, cb); } else { - AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. - result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params, cb); + // + // Delegate to the subclass. + // + pair<const Ice::Byte*, const Ice::Byte*> p(&_results[0], &_results[0] + _results.size()); + handleResponse(future.get(), _ok, p); + if(PyErr_Occurred()) + { + return 0; + } } - - sentSynchronously = result->sentSynchronously(); } - catch(const Ice::CommunicatorDestroyedException& ex) + + _future = future.release(); + return incRef(_future); +} + +void +IcePy::NewAsyncInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) { // - // CommunicatorDestroyedException can propagate directly. + // The future hasn't been created yet, which means invoke() is still running. Save the results for later. // - setPythonException(ex); - return 0; + _ok = ok; + vector<Ice::Byte> v(results.first, results.second); + _results.swap(v); + _done = true; + return; + } + + PyObjectHandle future = _future; // Steals a reference. + + if(_sent) + { + _future = 0; // Break cyclic dependency. } - catch(const Ice::TwowayOnlyException& ex) + else { + assert(!_done); + // - // Raised by checkTwowayOnly. + // The sent callback will release our reference. // - callException(_callback, _op->name, "ice_exception", ex); + Py_INCREF(_future); } - catch(const Ice::Exception&) + + _done = true; + + // + // Delegate to the subclass. + // + handleResponse(future.get(), ok, results); + if(PyErr_Occurred()) + { + handleException(); + } +} + +void +IcePy::NewAsyncInvocation::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) { // - // No other exceptions should be raised by begin_ice_invoke. + // The future hasn't been created yet, which means invoke() is still running. Save the exception for later. // - assert(false); + _exception = convertException(ex); + _done = true; + return; } - PyRETURN_BOOL(sentSynchronously); + PyObjectHandle future = _future; // Steals a reference. + _future = 0; // Break cyclic dependency. + _done = true; + + PyObjectHandle exh = convertException(ex); + assert(exh.get()); + PyObjectHandle tmp = callMethod(future.get(), "set_exception", exh.get()); + if(PyErr_Occurred()) + { + handleException(); + } } void -IcePy::OldAsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +IcePy::NewAsyncInvocation::sent(bool sentSynchronously) { AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - assert(_callback); + if(!_future) + { + // + // The future hasn't been created yet, which means invoke() is still running. + // + _sent = true; + _sentSynchronously = sentSynchronously; + return; + } + + PyObjectHandle future = _future; + + if(_done || !_twoway) + { + _future = 0; // Break cyclic dependency. + } + else + { + _sent = true; + + // + // The reference to _future will be released in response() or exception(). + // + Py_INCREF(_future); + } + + PyObjectHandle tmp = callMethod(future.get(), "set_sent", sentSynchronously ? getTrue() : getFalse()); + if(PyErr_Occurred()) + { + handleException(); + } + + if(!_twoway) + { + // + // For a oneway/datagram invocation, we consider it complete when sent. + // + tmp = callMethod(future.get(), "set_result", Py_None); + if(PyErr_Occurred()) + { + handleException(); + } + } +} + +// +// NewAsyncTypedInvocation +// +IcePy::NewAsyncTypedInvocation::NewAsyncTypedInvocation(const Ice::ObjectPrx& prx, PyObject* pyProxy, + const OperationPtr& op) + : NewAsyncInvocation(prx, pyProxy, op->name), _op(op) +{ +} + +Ice::AsyncResultPtr +IcePy::NewAsyncTypedInvocation::handleInvoke(PyObject* args, PyObject* /* kwds */) +{ + // + // Called from Python code, so the GIL is already acquired. + // + + assert(PyTuple_Check(args)); + assert(PyTuple_GET_SIZE(args) == 2); // Format is ((params...), context|None) + PyObject* pyparams = PyTuple_GET_ITEM(args, 0); + assert(PyTuple_Check(pyparams)); + PyObject* pyctx = PyTuple_GET_ITEM(args, 1); + + // + // Marshal the input parameters to a byte sequence. + // + Ice::OutputStream os(_communicator); + pair<const Ice::Byte*, const Ice::Byte*> params; + if(!prepareRequest(_op, pyparams, NewAsyncMapping, &os, params)) + { + return 0; + } + + checkAsyncTwowayOnly(_prx); + + NewAsyncInvocationPtr self = this; + Ice::Callback_Object_ice_invokePtr cb = + Ice::newCallback_Object_ice_invoke(self, &NewAsyncInvocation::response, &NewAsyncInvocation::exception, + &NewAsyncInvocation::sent); + + // + // Invoke the operation asynchronously. + // + if(pyctx != Py_None) + { + Ice::Context ctx; + + if(!PyDict_Check(pyctx)) + { + PyErr_Format(PyExc_ValueError, STRCAST("context argument must be None or a dictionary")); + return 0; + } + + if(!dictionaryToContext(pyctx, ctx)) + { + return 0; + } + + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + return _prx->begin_ice_invoke(_op->name, _op->sendMode, params, ctx, cb); + } + else + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + return _prx->begin_ice_invoke(_op->name, _op->sendMode, params, cb); + } +} +void +IcePy::NewAsyncTypedInvocation::handleResponse(PyObject* future, bool ok, + const pair<const Ice::Byte*, const Ice::Byte*>& results) +{ try { if(ok) @@ -2379,69 +2771,70 @@ IcePy::OldAsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, c // Unmarshal the results. // PyObjectHandle args; + try { - args = unmarshalResults(results); + args = unmarshalResults(_op, results); if(!args.get()) { assert(PyErr_Occurred()); - PyErr_Print(); return; } } catch(const Ice::Exception& ex) { - callException(_callback, _op->name, "ice_exception", ex); + PyObjectHandle exh = convertException(ex); + assert(exh.get()); + PyObjectHandle tmp = callMethod(future, "set_exception", exh.get()); + PyErr_Clear(); return; } - const string methodName = "ice_response"; - if(!PyObject_HasAttrString(_callback, STRCAST(methodName.c_str()))) + // + // The future's result is always one value: + // + // - If the operation has no out parameters, the result is None + // - If the operation returns one value, the result is the value + // - If the operation returns multiple values, the result is a tuple containing the values + // + PyObjectHandle r; + if(PyTuple_GET_SIZE(args.get()) == 0) { - ostringstream ostr; - ostr << "AMI callback object for operation `" << _op->name << "' does not define " << methodName - << "()"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + r = incRef(Py_None); + } + else if(PyTuple_GET_SIZE(args.get()) == 1) + { + r = incRef(PyTuple_GET_ITEM(args.get(), 0)); // PyTuple_GET_ITEM steals a reference. } else { - PyObjectHandle method = PyObject_GetAttrString(_callback, STRCAST(methodName.c_str())); - assert(method.get()); - PyObjectHandle tmp = PyObject_Call(method.get(), args.get(), 0); - if(PyErr_Occurred()) - { - handleException(); // Callback raised an exception. - } + r = args; } + + PyObjectHandle tmp = callMethod(future, "set_result", r.get()); + PyErr_Clear(); } else { - PyObjectHandle ex = unmarshalException(results); - callException(_callback, _op->name, "ice_exception", ex.get()); + PyObjectHandle ex = unmarshalException(_op, results); + PyObjectHandle tmp = callMethod(future, "set_exception", ex.get()); + PyErr_Clear(); } } catch(const AbortMarshaling&) { assert(PyErr_Occurred()); - PyErr_Print(); } } void -IcePy::OldAsyncTypedInvocation::exception(const Ice::Exception& ex) +IcePy::NewAsyncTypedInvocation::checkAsyncTwowayOnly(const Ice::ObjectPrx& proxy) const { - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - - callException(_callback, _op->name, "ice_exception", ex); -} - -void -IcePy::OldAsyncTypedInvocation::sent(bool) -{ - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - - callSent(_callback, "ice_sent", false, false); + if((_op->returnType != 0 || !_op->outParams.empty() || !_op->exceptions.empty()) && !proxy->ice_isTwoway()) + { + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, + "`" + _op->name + "' can only be called with a twoway proxy"); + } } // @@ -2574,11 +2967,7 @@ IcePy::SyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) } #endif - if(PyTuple_SET_ITEM(result.get(), 1, op.get()) < 0) - { - throwPythonException(); - } - op.release(); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(result.get(), 1, op.release()); // PyTuple_SET_ITEM steals a reference. return result.release(); } @@ -2652,8 +3041,7 @@ IcePy::AsyncBlobjectInvocation::invoke(PyObject* args, PyObject* kwds) if(PyCallable_Check(response)) { - _response = response; - Py_INCREF(_response); + _response = incRef(response); } else if(response != Py_None) { @@ -2663,8 +3051,7 @@ IcePy::AsyncBlobjectInvocation::invoke(PyObject* args, PyObject* kwds) if(PyCallable_Check(ex)) { - _ex = ex; - Py_INCREF(_ex); + _ex = incRef(ex); } else if(ex != Py_None) { @@ -2674,8 +3061,7 @@ IcePy::AsyncBlobjectInvocation::invoke(PyObject* args, PyObject* kwds) if(PyCallable_Check(sent)) { - _sent = sent; - Py_INCREF(_sent); + _sent = incRef(sent); } else if(sent != Py_None) { @@ -2787,8 +3173,7 @@ IcePy::AsyncBlobjectInvocation::invoke(PyObject* args, PyObject* kwds) } obj->result = new Ice::AsyncResultPtr(result); obj->invocation = new InvocationPtr(this); - obj->proxy = _pyProxy; - Py_INCREF(obj->proxy); + obj->proxy = incRef(_pyProxy); obj->communicator = getCommunicatorWrapper(_prx->ice_getCommunicator()); return reinterpret_cast<PyObject*>(obj); } @@ -2855,11 +3240,7 @@ IcePy::AsyncBlobjectInvocation::end(const Ice::ObjectPrx& proxy, const Ice::Asyn memcpy(buf, results.first, sz); #endif - if(PyTuple_SET_ITEM(args.get(), 1, op.get()) < 0) - { - return 0; - } - op.release(); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(args.get(), 1, op.release()); // PyTuple_SET_ITEM steals a reference. return args.release(); } @@ -2946,13 +3327,7 @@ IcePy::AsyncBlobjectInvocation::response(bool ok, const pair<const Ice::Byte*, c memcpy(buf, results.first, sz); #endif - if(PyTuple_SET_ITEM(args.get(), 1, op.get()) < 0) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } - op.release(); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(args.get(), 1, op.release()); // PyTuple_SET_ITEM steals a reference. PyObjectHandle tmp = PyObject_Call(_response, args.get(), 0); if(PyErr_Occurred()) @@ -2981,22 +3356,15 @@ IcePy::AsyncBlobjectInvocation::sent(bool sentSynchronously) } // -// OldAsyncBlobjectInvocation +// NewAsyncBlobjectInvocation // -IcePy::OldAsyncBlobjectInvocation::OldAsyncBlobjectInvocation(const Ice::ObjectPrx& prx) : - Invocation(prx), _callback(0) -{ -} - -IcePy::OldAsyncBlobjectInvocation::~OldAsyncBlobjectInvocation() +IcePy::NewAsyncBlobjectInvocation::NewAsyncBlobjectInvocation(const Ice::ObjectPrx& prx, PyObject* pyProxy) : + NewAsyncInvocation(prx, pyProxy, "ice_invoke") { - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - - Py_XDECREF(_callback); } -PyObject* -IcePy::OldAsyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) +Ice::AsyncResultPtr +IcePy::NewAsyncBlobjectInvocation::handleInvoke(PyObject* args, PyObject* /* kwds */) { char* operation; PyObject* mode; @@ -3004,20 +3372,19 @@ IcePy::OldAsyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) PyObject* operationModeType = lookupType("Ice.OperationMode"); PyObject* ctx = 0; #if PY_VERSION_HEX >= 0x03000000 - if(!PyArg_ParseTuple(args, STRCAST("OsO!O!|O"), &_callback, &operation, operationModeType, &mode, + if(!PyArg_ParseTuple(args, STRCAST("sO!O!|O"), &operation, operationModeType, &mode, &PyBytes_Type, &inParams, &ctx)) { return 0; } #else - if(!PyArg_ParseTuple(args, STRCAST("OsO!O!|O"), &_callback, &operation, operationModeType, &mode, + if(!PyArg_ParseTuple(args, STRCAST("sO!O!|O"), &operation, operationModeType, &mode, &PyBuffer_Type, &inParams, &ctx)) { return 0; } #endif - Py_INCREF(_callback); _op = operation; PyObjectHandle modeValue = PyObject_GetAttrString(mode, STRCAST("value")); @@ -3049,169 +3416,175 @@ IcePy::OldAsyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) } #endif - bool sentSynchronously = false; - try - { - Ice::AsyncResultPtr result; - Ice::Callback_Object_ice_invokePtr cb = - Ice::newCallback_Object_ice_invoke(this, &OldAsyncBlobjectInvocation::response, - &OldAsyncBlobjectInvocation::exception, - &OldAsyncBlobjectInvocation::sent); + NewAsyncInvocationPtr self = this; + Ice::Callback_Object_ice_invokePtr cb = + Ice::newCallback_Object_ice_invoke(self, &NewAsyncInvocation::response, &NewAsyncInvocation::exception, + &NewAsyncInvocation::sent); - if(ctx == 0 || ctx == Py_None) - { - AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. - result = _prx->begin_ice_invoke(operation, sendMode, in, cb); - } - else - { - Ice::Context context; - if(!dictionaryToContext(ctx, context)) - { - return 0; - } - - AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. - result = _prx->begin_ice_invoke(operation, sendMode, in, context, cb); - } - - sentSynchronously = result->sentSynchronously(); - } - catch(const Ice::CommunicatorDestroyedException& ex) + if(ctx == 0 || ctx == Py_None) { // - // CommunicatorDestroyedException is the only exception that can propagate directly. + // Don't release the GIL here. We want other threads to block until we've had a chance + // to create the future. // - setPythonException(ex); - return 0; + //AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + return _prx->begin_ice_invoke(operation, sendMode, in, cb); } - catch(const Ice::Exception&) + else { + Ice::Context context; + if(!dictionaryToContext(ctx, context)) + { + return 0; + } + // - // No other exceptions should be raised by begin_ice_invoke. + // Don't release the GIL here. We want other threads to block until we've had a chance + // to create the future. // - assert(false); + //AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + return _prx->begin_ice_invoke(operation, sendMode, in, context, cb); } - - PyRETURN_BOOL(sentSynchronously); } void -IcePy::OldAsyncBlobjectInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +IcePy::NewAsyncBlobjectInvocation::handleResponse(PyObject* future, bool ok, + const pair<const Ice::Byte*, const Ice::Byte*>& results) { - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. - - try + // + // Prepare the args as a tuple of the bool and out param buffer. + // + PyObjectHandle args = PyTuple_New(2); + if(!args.get()) { - // - // Prepare the args as a tuple of the bool and out param buffer. - // - PyObjectHandle args = PyTuple_New(2); - if(!args.get()) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } - if(PyTuple_SET_ITEM(args.get(), 0, ok ? incTrue() : incFalse()) < 0) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } + if(PyTuple_SET_ITEM(args.get(), 0, ok ? incTrue() : incFalse()) < 0) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } #if PY_VERSION_HEX >= 0x03000000 - Py_ssize_t sz = results.second - results.first; - PyObjectHandle op; - if(sz == 0) - { - op = PyBytes_FromString(""); - } - else - { - op = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(results.first), sz); - } - if(!op.get()) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } + Py_ssize_t sz = results.second - results.first; + PyObjectHandle op; + if(sz == 0) + { + op = PyBytes_FromString(""); + } + else + { + op = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(results.first), sz); + } + if(!op.get()) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } #else - // - // Create the output buffer and copy in the outParams. - // - PyObjectHandle op = PyBuffer_New(results.second - results.first); - if(!op.get()) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } + // + // Create the output buffer and copy in the outParams. + // + PyObjectHandle op = PyBuffer_New(results.second - results.first); + if(!op.get()) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } - void* buf; - Py_ssize_t sz; - if(PyObject_AsWriteBuffer(op.get(), &buf, &sz)) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } - assert(sz == results.second - results.first); - memcpy(buf, results.first, sz); + void* buf; + Py_ssize_t sz; + if(PyObject_AsWriteBuffer(op.get(), &buf, &sz)) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } + assert(sz == results.second - results.first); + memcpy(buf, results.first, sz); #endif - if(PyTuple_SET_ITEM(args.get(), 1, op.get()) < 0) - { - assert(PyErr_Occurred()); - PyErr_Print(); - return; - } - op.release(); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(args.get(), 1, op.release()); // PyTuple_SET_ITEM steals a reference. - const string methodName = "ice_response"; - if(!PyObject_HasAttrString(_callback, STRCAST(methodName.c_str()))) - { - ostringstream ostr; - ostr << "AMI callback object for operation `ice_invoke_async' does not define " << methodName << "()"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - } - else - { - PyObjectHandle method = PyObject_GetAttrString(_callback, STRCAST(methodName.c_str())); - assert(method.get()); - PyObjectHandle tmp = PyObject_Call(method.get(), args.get(), 0); - if(PyErr_Occurred()) - { - handleException(); // Callback raised an exception. - } - } + PyObjectHandle tmp = callMethod(future, "set_result", args.get()); + PyErr_Clear(); +} + +// +// Upcall +// +void +Upcall::dispatchImpl(PyObject* servant, const string& dispatchName, PyObject* args, const Ice::Current& current) +{ + Ice::CommunicatorPtr communicator = current.adapter->getCommunicator(); + + // + // Find the servant method for the operation. Use dispatchName here, not current.operation. + // + PyObjectHandle servantMethod = PyObject_GetAttrString(servant, const_cast<char*>(dispatchName.c_str())); + if(!servantMethod.get()) + { + ostringstream ostr; + ostr << "servant for identity " << communicator->identityToString(current.id) + << " does not define operation `" << dispatchName << "'"; + string str = ostr.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + Ice::UnknownException ex(__FILE__, __LINE__); + ex.unknown = str; + throw ex; } - catch(const Ice::Exception& ex) + + // + // Get the _dispatch method. The _dispatch method will invoke the servant method and pass it the arguments. + // + PyObjectHandle dispatchMethod = PyObject_GetAttrString(servant, STRCAST("_dispatch")); + if(!dispatchMethod.get()) { ostringstream ostr; - ostr << "Exception raised by AMI callback for operation `ice_invoke_async':" << ex; + ostr << "_dispatch method not found for identity " << communicator->identityToString(current.id) + << " and operation `" << dispatchName << "'"; string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + Ice::UnknownException ex(__FILE__, __LINE__); + ex.unknown = str; + throw ex; } -} -void -IcePy::OldAsyncBlobjectInvocation::exception(const Ice::Exception& ex) -{ - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + PyObjectHandle dispatchArgs = PyTuple_New(3); + if(!dispatchArgs.get()) + { + throwPythonException(); + } - callException(_callback, "ice_invoke", "ice_exception", ex); -} + DispatchCallbackObject* callback = dispatchCallbackNew(&DispatchCallbackType, 0, 0); + if(!callback) + { + throwPythonException(); + } + callback->upcall = new UpcallPtr(this); + PyTuple_SET_ITEM(dispatchArgs.get(), 0, reinterpret_cast<PyObject*>(callback)); // Steals a reference. + PyTuple_SET_ITEM(dispatchArgs.get(), 1, servantMethod.release()); // Steals a reference. + PyTuple_SET_ITEM(dispatchArgs.get(), 2, incRef(args)); // Steals a reference. -void -IcePy::OldAsyncBlobjectInvocation::sent(bool) -{ - AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + // + // Ignore the return value of _dispatch -- it will use the dispatch callback. + // + PyObjectHandle ignore = PyObject_Call(dispatchMethod.get(), dispatchArgs.get(), 0); - callSent(_callback, "ice_sent", false, false); + // + // Check for exceptions. + // + if(PyErr_Occurred()) + { + PyException ex; // Retrieve it before another Python API call clears it. + exception(ex); + } } // @@ -3219,7 +3592,7 @@ IcePy::OldAsyncBlobjectInvocation::sent(bool) // IcePy::TypedUpcall::TypedUpcall(const OperationPtr& op, const Ice::AMD_Object_ice_invokePtr& callback, const Ice::CommunicatorPtr& communicator) : - _op(op), _callback(callback), _communicator(communicator), _finished(false) + _op(op), _callback(callback), _communicator(communicator) { } @@ -3227,19 +3600,14 @@ void IcePy::TypedUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, const Ice::Byte*>& inBytes, const Ice::Current& current) { + _encoding = current.encoding; + // // Unmarshal the in parameters. We have to leave room in the arguments for a trailing // Ice::Current object. // Py_ssize_t count = static_cast<Py_ssize_t>(_op->inParams.size()) + 1; - Py_ssize_t offset = 0; - if(_op->amd) - { - ++count; // Leave room for a leading AMD callback argument. - offset = 1; - } - PyObjectHandle args = PyTuple_New(count); if(!args.get()) { @@ -3272,7 +3640,7 @@ IcePy::TypedUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, con ParamInfoPtr info = *p; if(!info->optional) { - void* closure = reinterpret_cast<void*>(info->pos + offset); + void* closure = reinterpret_cast<void*>(info->pos); info->type->unmarshal(&is, info, args.get(), closure, false, &info->metaData); } } @@ -3285,16 +3653,12 @@ IcePy::TypedUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, con ParamInfoPtr info = *p; if(is.readOptional(info->tag, info->type->optionalFormat())) { - void* closure = reinterpret_cast<void*>(info->pos + offset); + void* closure = reinterpret_cast<void*>(info->pos); info->type->unmarshal(&is, info, args.get(), closure, true, &info->metaData); } else { - if(PyTuple_SET_ITEM(args.get(), info->pos + offset, Unset) < 0) - { - throwPythonException(); - } - Py_INCREF(Unset); // PyTuple_SET_ITEM steals a reference. + PyTuple_SET_ITEM(args.get(), info->pos, incRef(Unset)); // PyTuple_SET_ITEM steals a reference. } } @@ -3317,209 +3681,149 @@ IcePy::TypedUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, con // Create an object to represent Ice::Current. We need to append this to the argument tuple. // PyObjectHandle curr = createCurrent(current); - if(PyTuple_SET_ITEM(args.get(), PyTuple_GET_SIZE(args.get()) - 1, curr.get()) < 0) - { - throwPythonException(); - } - curr.release(); // PyTuple_SET_ITEM steals a reference. - - if(_op->amd) - { - // - // Create the callback object and pass it as the first argument. - // - AMDCallbackObject* obj = amdCallbackNew(&AMDCallbackType, 0, 0); - if(!obj) - { - throwPythonException(); - } - obj->upcall = new UpcallPtr(this); - obj->encoding = current.encoding; - if(PyTuple_SET_ITEM(args.get(), 0, (PyObject*)obj) < 0) // PyTuple_SET_ITEM steals a reference. - { - Py_DECREF(obj); - throwPythonException(); - } - } - - // - // Dispatch the operation. Use _dispatchName here, not current.operation. - // - PyObjectHandle method = PyObject_GetAttrString(servant, const_cast<char*>(_op->dispatchName.c_str())); - if(!method.get()) - { - ostringstream ostr; - ostr << "servant for identity " << _communicator->identityToString(current.id) - << " does not define operation `" << _op->dispatchName << "'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - Ice::UnknownException ex(__FILE__, __LINE__); - ex.unknown = str; - throw ex; - } - - PyObjectHandle result = PyObject_Call(method.get(), args.get(), 0); - - // - // Check for exceptions. - // - if(PyErr_Occurred()) - { - PyException ex; // Retrieve it before another Python API call clears it. - exception(ex, current.encoding); - return; - } + PyTuple_SET_ITEM(args.get(), PyTuple_GET_SIZE(args.get()) - 1, + curr.release()); // PyTuple_SET_ITEM steals a reference. - if(!_op->amd) - { - response(result.get(), current.encoding); - } + dispatchImpl(servant, _op->dispatchName, args.get(), current); } void -IcePy::TypedUpcall::response(PyObject* args, const Ice::EncodingVersion& encoding) +IcePy::TypedUpcall::response(PyObject* result) { - if(_finished) + try { // - // This method could be called more than once if the application calls - // ice_response multiple times. We ignore subsequent calls. + // Marshal the results. If there is more than one value to be returned, then they must be + // returned in a tuple of the form (result, outParam1, ...). // - return; - } - _finished = true; + Py_ssize_t numResults = static_cast<Py_ssize_t>(_op->outParams.size()); + if(_op->returnType) + { + numResults++; + } + + if(numResults > 1 && (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != numResults)) + { + ostringstream ostr; + ostr << "operation `" << fixIdent(_op->name) << "' should return a tuple of length " << numResults; + string str = ostr.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + throw Ice::MarshalException(__FILE__, __LINE__); + } - try - { // - // Marshal the results. If there is more than one value to be returned, then they must be - // returned in a tuple of the form (result, outParam1, ...). + // Normalize the result value. When there are multiple result values, result is already a tuple. + // Otherwise, we create a tuple to make the code a little simpler. // - Ice::OutputStream os(_communicator); - try + PyObjectHandle t; + if(numResults > 1) + { + t = incRef(result); + } + else { - Py_ssize_t numResults = static_cast<Py_ssize_t>(_op->outParams.size()); - if(_op->returnType) + t = PyTuple_New(1); + if(!t.get()) { - numResults++; + throw AbortMarshaling(); } + PyTuple_SET_ITEM(t.get(), 0, incRef(result)); + } + + ObjectMap objectMap; + ParamInfoList::iterator p; - if(numResults > 1 && (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) != numResults)) + // + // Validate the results. + // + for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) + { + ParamInfoPtr info = *p; + PyObject* arg = PyTuple_GET_ITEM(t.get(), info->pos); + if((!info->optional || arg != Unset) && !info->type->validate(arg)) { + // TODO: Provide the parameter name instead? ostringstream ostr; - ostr << "operation `" << fixIdent(_op->name) << "' should return a tuple of length " << numResults; + ostr << "invalid value for out argument " << (info->pos + 1) << " in operation `" + << _op->dispatchName << "'"; string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); throw Ice::MarshalException(__FILE__, __LINE__); } - - // - // Normalize the args value. For an AMD operation, or when there are multiple - // result values, args is already a tuple. Otherwise, we create a tuple to - // make the code a little simpler. - // - PyObjectHandle t; - if(_op->amd || numResults > 1) - { - t = args; - } - else + } + if(_op->returnType) + { + PyObject* res = PyTuple_GET_ITEM(t.get(), 0); + if((!_op->returnType->optional || res != Unset) && !_op->returnType->type->validate(res)) { - t = PyTuple_New(1); - if(!t.get()) - { - throw AbortMarshaling(); - } - PyTuple_SET_ITEM(t.get(), 0, args); + ostringstream ostr; + ostr << "invalid return value for operation `" << _op->dispatchName << "'"; + string str = ostr.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + throw Ice::MarshalException(__FILE__, __LINE__); } - Py_INCREF(args); - - os.startEncapsulation(encoding, _op->format); + } - ObjectMap objectMap; - ParamInfoList::iterator p; + Ice::OutputStream os(_communicator); + os.startEncapsulation(_encoding, _op->format); - // - // Validate the results. - // - for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) + // + // Marshal the required out parameters. + // + for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) + { + ParamInfoPtr info = *p; + if(!info->optional) { - ParamInfoPtr info = *p; PyObject* arg = PyTuple_GET_ITEM(t.get(), info->pos); - if((!info->optional || arg != Unset) && !info->type->validate(arg)) - { - // TODO: Provide the parameter name instead? - ostringstream ostr; - ostr << "invalid value for out argument " << (info->pos + 1) << " in operation `" - << _op->dispatchName << "'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - throw Ice::MarshalException(__FILE__, __LINE__); - } - } - if(_op->returnType) - { - PyObject* res = PyTuple_GET_ITEM(t.get(), 0); - if((!_op->returnType->optional || res != Unset) && !_op->returnType->type->validate(res)) - { - ostringstream ostr; - ostr << "invalid return value for operation `" << _op->dispatchName << "'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - throw Ice::MarshalException(__FILE__, __LINE__); - } - } - - // - // Marshal the required out parameters. - // - for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) - { - ParamInfoPtr info = *p; - if(!info->optional) - { - PyObject* arg = PyTuple_GET_ITEM(t.get(), info->pos); - info->type->marshal(arg, &os, &objectMap, false, &info->metaData); - } + info->type->marshal(arg, &os, &objectMap, false, &info->metaData); } + } - // - // Marshal the required return value, if any. - // - if(_op->returnType && !_op->returnType->optional) - { - PyObject* res = PyTuple_GET_ITEM(t.get(), 0); - _op->returnType->type->marshal(res, &os, &objectMap, false, &_op->metaData); - } + // + // Marshal the required return value, if any. + // + if(_op->returnType && !_op->returnType->optional) + { + PyObject* res = PyTuple_GET_ITEM(t.get(), 0); + _op->returnType->type->marshal(res, &os, &objectMap, false, &_op->metaData); + } - // - // Marshal the optional results. - // - for(p = _op->optionalOutParams.begin(); p != _op->optionalOutParams.end(); ++p) + // + // Marshal the optional results. + // + for(p = _op->optionalOutParams.begin(); p != _op->optionalOutParams.end(); ++p) + { + ParamInfoPtr info = *p; + PyObject* arg = PyTuple_GET_ITEM(t.get(), info->pos); + if(arg != Unset && os.writeOptional(info->tag, info->type->optionalFormat())) { - ParamInfoPtr info = *p; - PyObject* arg = PyTuple_GET_ITEM(t.get(), info->pos); - if(arg != Unset && os.writeOptional(info->tag, info->type->optionalFormat())) - { - info->type->marshal(arg, &os, &objectMap, true, &info->metaData); - } + info->type->marshal(arg, &os, &objectMap, true, &info->metaData); } + } - if(_op->returnsClasses) - { - os.writePendingValues(); - } + if(_op->returnsClasses) + { + os.writePendingValues(); + } - os.endEncapsulation(); + os.endEncapsulation(); - AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. - _callback->ice_response(true, os.finished()); - } - catch(const AbortMarshaling&) + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_response(true, os.finished()); + } + catch(const AbortMarshaling&) + { + try { throwPythonException(); } + catch(const Ice::Exception& ex) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_exception(ex); + } } catch(const Ice::Exception& ex) { @@ -3529,19 +3833,8 @@ IcePy::TypedUpcall::response(PyObject* args, const Ice::EncodingVersion& encodin } void -IcePy::TypedUpcall::exception(PyException& ex, const Ice::EncodingVersion& encoding) +IcePy::TypedUpcall::exception(PyException& ex) { - if(_finished) - { - // - // An asynchronous response or exception has already been sent. We just - // raise an exception and let the C++ run time handle it. - // - ex.raise(); - } - - _finished = true; - try { try @@ -3567,7 +3860,7 @@ IcePy::TypedUpcall::exception(PyException& ex, const Ice::EncodingVersion& encod assert(info); Ice::OutputStream os(_communicator); - os.startEncapsulation(encoding, _op->format); + os.startEncapsulation(_encoding, _op->format); ExceptionWriter writer(ex.ex, info); os.writeException(writer); @@ -3589,16 +3882,22 @@ IcePy::TypedUpcall::exception(PyException& ex, const Ice::EncodingVersion& encod } catch(const Ice::Exception& ex) { - AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. - _callback->ice_exception(ex); + exception(ex); } } +void +IcePy::TypedUpcall::exception(const Ice::Exception& ex) +{ + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_exception(ex); +} + // // BlobjectUpcall // -IcePy::BlobjectUpcall::BlobjectUpcall(bool amd, const Ice::AMD_Object_ice_invokePtr& callback) : - _amd(amd), _callback(callback), _finished(false) +IcePy::BlobjectUpcall::BlobjectUpcall(const Ice::AMD_Object_ice_invokePtr& callback) : + _callback(callback) { } @@ -3611,11 +3910,6 @@ IcePy::BlobjectUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, Py_ssize_t count = 2; // First is the inParams, second is the Ice::Current object. Py_ssize_t start = 0; - if(_amd) - { - ++count; // Leave room for a leading AMD callback argument. - start = 1; - } PyObjectHandle args = PyTuple_New(count); if(!args.get()) @@ -3636,196 +3930,113 @@ IcePy::BlobjectUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, } #else // - // If using AMD we need to copy the bytes since the bytes may be - // accessed after this method is over, otherwise - // PyBuffer_FromMemory can be used which doesn't do a copy. + // Make a copy of the bytes since the bytes may be accessed after this method is over. // - if(!_amd) + ip = PyBuffer_New(inBytes.second - inBytes.first); + if(!ip.get()) { - ip = PyBuffer_FromMemory((void*)inBytes.first, inBytes.second - inBytes.first); - if(!ip.get()) - { - throwPythonException(); - } + throwPythonException(); } - else + void* buf; + Py_ssize_t sz; + if(PyObject_AsWriteBuffer(ip.get(), &buf, &sz)) { - ip = PyBuffer_New(inBytes.second - inBytes.first); - if(!ip.get()) - { - throwPythonException(); - } - void* buf; - Py_ssize_t sz; - if(PyObject_AsWriteBuffer(ip.get(), &buf, &sz)) - { - throwPythonException(); - } - assert(sz == inBytes.second - inBytes.first); - memcpy(buf, inBytes.first, sz); + throwPythonException(); } + assert(sz == inBytes.second - inBytes.first); + memcpy(buf, inBytes.first, sz); #endif - if(PyTuple_SET_ITEM(args.get(), start, ip.get()) < 0) - { - throwPythonException(); - } + PyTuple_SET_ITEM(args.get(), start, ip.release()); // PyTuple_SET_ITEM steals a reference. ++start; - ip.release(); // PyTuple_SET_ITEM steals a reference. // // Create an object to represent Ice::Current. We need to append // this to the argument tuple. // PyObjectHandle curr = createCurrent(current); - if(PyTuple_SET_ITEM(args.get(), start, curr.get()) < 0) - { - throwPythonException(); - } - curr.release(); // PyTuple_SET_ITEM steals a reference. - - string dispatchName = "ice_invoke"; - if(_amd) - { - dispatchName += "_async"; - // - // Create the callback object and pass it as the first argument. - // - AMDCallbackObject* obj = amdCallbackNew(&AMDCallbackType, 0, 0); - if(!obj) - { - throwPythonException(); - } - obj->upcall = new UpcallPtr(this); - obj->encoding = current.encoding; - if(PyTuple_SET_ITEM(args.get(), 0, (PyObject*)obj) < 0) // PyTuple_SET_ITEM steals a reference. - { - Py_DECREF(obj); - throwPythonException(); - } - } - - // - // Dispatch the operation. - // - PyObjectHandle method = PyObject_GetAttrString(servant, const_cast<char*>(dispatchName.c_str())); - if(!method.get()) - { - ostringstream ostr; - ostr << "servant for identity " << communicator->identityToString(current.id) - << " does not define operation `" << dispatchName << "'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - Ice::UnknownException ex(__FILE__, __LINE__); - ex.unknown = str; - throw ex; - } - - PyObjectHandle result = PyObject_Call(method.get(), args.get(), 0); - - // - // Check for exceptions. - // - if(PyErr_Occurred()) - { - PyException ex; // Retrieve it before another Python API call clears it. - exception(ex, current.encoding); - return; - } + PyTuple_SET_ITEM(args.get(), start, curr.release()); // PyTuple_SET_ITEM steals a reference. - if(!_amd) - { - response(result.get(), current.encoding); - } + dispatchImpl(servant, "ice_invoke", args.get(), current); } void -IcePy::BlobjectUpcall::response(PyObject* args, const Ice::EncodingVersion&) +IcePy::BlobjectUpcall::response(PyObject* result) { - if(_finished) + try { // - // This method could be called more than once if the application calls - // ice_response multiple times. We ignore subsequent calls. + // The result is a tuple of (bool, results). // - return; - } + if(!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 2) + { + string str = "operation `ice_invoke' should return a tuple of length 2"; + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + throw Ice::MarshalException(__FILE__, __LINE__); + } - _finished = true; + PyObject* arg = PyTuple_GET_ITEM(result, 0); + bool isTrue = PyObject_IsTrue(arg) == 1; - // - // The return value is a tuple of (bool, results). - // - if(!PyTuple_Check(args) || PyTuple_GET_SIZE(args) != 2) - { - ostringstream ostr; - string name = "ice_invoke"; - if(_amd) + arg = PyTuple_GET_ITEM(result, 1); + +#if PY_VERSION_HEX >= 0x03000000 + if(!PyBytes_Check(arg)) { - name += "_async"; + ostringstream ostr; + ostr << "invalid return value for operation `ice_invoke'"; + string str = ostr.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + throw Ice::MarshalException(__FILE__, __LINE__); } - ostr << "operation `" << name << "' should return a tuple of length 2"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - throw Ice::MarshalException(__FILE__, __LINE__); - } - PyObject* arg = PyTuple_GET_ITEM(args, 0); - bool isTrue = PyObject_IsTrue(arg) == 1; + Py_ssize_t sz = PyBytes_GET_SIZE(arg); + pair<const ::Ice::Byte*, const ::Ice::Byte*> r(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + r.first = reinterpret_cast<Ice::Byte*>(PyBytes_AS_STRING(arg)); + r.second = r.first + sz; + } +#else + if(!PyBuffer_Check(arg)) + { + ostringstream ostr; + ostr << "invalid return value for operation `ice_invoke'"; + string str = ostr.str(); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); + throw Ice::MarshalException(__FILE__, __LINE__); + } - arg = PyTuple_GET_ITEM(args, 1); + char* charBuf = 0; + Py_ssize_t sz = arg->ob_type->tp_as_buffer->bf_getcharbuffer(arg, 0, &charBuf); + const Ice::Byte* mem = reinterpret_cast<const Ice::Byte*>(charBuf); + const pair<const ::Ice::Byte*, const ::Ice::Byte*> r(mem, mem + sz); +#endif -#if PY_VERSION_HEX >= 0x03000000 - if(!PyBytes_Check(arg)) - { - ostringstream ostr; - ostr << "invalid return value for operation `ice_invoke'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - throw Ice::MarshalException(__FILE__, __LINE__); + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_response(isTrue, r); } - - Py_ssize_t sz = PyBytes_GET_SIZE(arg); - pair<const ::Ice::Byte*, const ::Ice::Byte*> r(static_cast<const Ice::Byte*>(0),static_cast<const Ice::Byte*>(0)); - if(sz > 0) + catch(const AbortMarshaling&) { - r.first = reinterpret_cast<Ice::Byte*>(PyBytes_AS_STRING(arg)); - r.second = r.first + sz; + try + { + throwPythonException(); + } + catch(const Ice::Exception& ex) + { + exception(ex); + } } -#else - if(!PyBuffer_Check(arg)) + catch(const Ice::Exception& ex) { - ostringstream ostr; - ostr << "invalid return value for operation `ice_invoke'"; - string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); - throw Ice::MarshalException(__FILE__, __LINE__); + exception(ex); } - - char* charBuf = 0; - Py_ssize_t sz = arg->ob_type->tp_as_buffer->bf_getcharbuffer(arg, 0, &charBuf); - const Ice::Byte* mem = reinterpret_cast<const Ice::Byte*>(charBuf); - const pair<const ::Ice::Byte*, const ::Ice::Byte*> r(mem, mem + sz); -#endif - - AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. - _callback->ice_response(isTrue, r); } void -IcePy::BlobjectUpcall::exception(PyException& ex, const Ice::EncodingVersion&) +IcePy::BlobjectUpcall::exception(PyException& ex) { - if(_finished) - { - // - // An asynchronous response or exception has already been sent. We just - // raise an exception and let the C++ run time handle it. - // - ex.raise(); - } - - _finished = true; - try { // @@ -3840,11 +4051,17 @@ IcePy::BlobjectUpcall::exception(PyException& ex, const Ice::EncodingVersion&) } catch(const Ice::Exception& ex) { - AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. - _callback->ice_exception(ex); + exception(ex); } } +void +IcePy::BlobjectUpcall::exception(const Ice::Exception& ex) +{ + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_exception(ex); +} + PyObject* IcePy::invokeBuiltin(PyObject* proxy, const string& builtin, PyObject* args) { @@ -3863,6 +4080,23 @@ IcePy::invokeBuiltin(PyObject* proxy, const string& builtin, PyObject* args) } PyObject* +IcePy::invokeBuiltinAsync(PyObject* proxy, const string& builtin, PyObject* args) +{ + string name = "_op_" + builtin; + PyObject* objectType = lookupType("Ice.Object"); + assert(objectType); + PyObjectHandle obj = PyObject_GetAttrString(objectType, STRCAST(name.c_str())); + assert(obj.get()); + + OperationPtr op = getOperation(obj.get()); + assert(op); + + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new NewAsyncTypedInvocation(p, proxy, op); + return i->invoke(args); +} + +PyObject* IcePy::beginBuiltin(PyObject* proxy, const string& builtin, PyObject* args) { string name = "_op_" + builtin; @@ -3921,7 +4155,7 @@ PyObject* IcePy::iceInvokeAsync(PyObject* proxy, PyObject* args) { Ice::ObjectPrx p = getProxy(proxy); - InvocationPtr i = new OldAsyncBlobjectInvocation(p); + InvocationPtr i = new NewAsyncBlobjectInvocation(p, proxy); return i->invoke(args); } @@ -3963,12 +4197,9 @@ IcePy::createAsyncResult(const Ice::AsyncResultPtr& r, PyObject* proxy, PyObject return 0; } obj->result = new Ice::AsyncResultPtr(r); - obj->proxy = proxy; - Py_XINCREF(obj->proxy); - obj->connection = connection; - Py_XINCREF(obj->connection); - obj->communicator = communicator; - Py_XINCREF(obj->communicator); + obj->proxy = incRef(proxy); + obj->connection = incRef(connection); + obj->communicator = incRef(communicator); return reinterpret_cast<PyObject*>(obj); } @@ -3980,6 +4211,9 @@ IcePy::getAsyncResult(PyObject* p) return *obj->result; } +// +// FlushCallback +// IcePy::FlushCallback::FlushCallback(PyObject* ex, PyObject* sent, const string& op) : _ex(ex), _sent(sent), _op(op) { @@ -4014,6 +4248,100 @@ IcePy::FlushCallback::sent(bool sentSynchronously) } } +IcePy::FlushAsyncCallback::FlushAsyncCallback(const string& op) : + _op(op), _future(0), _sent(false), _sentSynchronously(false), _exception(0) +{ +} + +IcePy::FlushAsyncCallback::~FlushAsyncCallback() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_XDECREF(_future); + Py_XDECREF(_exception); +} + +void +IcePy::FlushAsyncCallback::setFuture(PyObject* future) +{ + // + // Called with the GIL locked. + // + + // + // Check if any callbacks have been invoked already. + // + if(_exception) + { + PyObjectHandle tmp = callMethod(future, "set_exception", _exception); + PyErr_Clear(); + } + else if(_sent) + { + PyObjectHandle tmp = callMethod(future, "set_sent", _sentSynchronously ? getTrue() : getFalse()); + PyErr_Clear(); + // + // We consider the invocation complete when sent. + // + tmp = callMethod(future, "set_result", Py_None); + PyErr_Clear(); + } + else + { + _future = incRef(future); + } +} + +void +IcePy::FlushAsyncCallback::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) + { + // + // The future hasn't been set yet, which means the request is still being invoked. Save the results for later. + // + _exception = convertException(ex); + return; + } + + PyObjectHandle exh = convertException(ex); + assert(exh.get()); + PyObjectHandle tmp = callMethod(_future, "set_exception", exh.get()); + PyErr_Clear(); + + Py_DECREF(_future); // Break cyclic dependency. + _future = 0; +} + +void +IcePy::FlushAsyncCallback::sent(bool sentSynchronously) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) + { + // + // The future hasn't been set yet, which means the request is still being invoked. Save the results for later. + // + _sent = true; + _sentSynchronously = sentSynchronously; + return; + } + + PyObjectHandle tmp = callMethod(_future, "set_sent", _sentSynchronously ? getTrue() : getFalse()); + PyErr_Clear(); + // + // We consider the invocation complete when sent. + // + tmp = callMethod(_future, "set_result", Py_None); + PyErr_Clear(); + + Py_DECREF(_future); // Break cyclic dependency. + _future = 0; +} + IcePy::GetConnectionCallback::GetConnectionCallback(const Ice::CommunicatorPtr& communicator, PyObject* response, PyObject* ex, const string& op) : _communicator(communicator), _response(response), _ex(ex), _op(op) @@ -4053,6 +4381,92 @@ IcePy::GetConnectionCallback::exception(const Ice::Exception& ex) callException(_ex, ex); } +IcePy::GetConnectionAsyncCallback::GetConnectionAsyncCallback(const Ice::CommunicatorPtr& communicator, + const string& op) : + _communicator(communicator), _op(op), _future(0), _exception(0) +{ +} + +IcePy::GetConnectionAsyncCallback::~GetConnectionAsyncCallback() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_XDECREF(_future); + Py_XDECREF(_exception); +} + +void +IcePy::GetConnectionAsyncCallback::setFuture(PyObject* future) +{ + // + // Called with the GIL locked. + // + + // + // Check if any callbacks have been invoked already. + // + if(_connection) + { + PyObjectHandle pyConn = createConnection(_connection, _communicator); + assert(pyConn.get()); + PyObjectHandle tmp = callMethod(future, "set_result", pyConn.get()); + PyErr_Clear(); + } + else if(_exception) + { + PyObjectHandle tmp = callMethod(future, "set_exception", _exception); + PyErr_Clear(); + } + else + { + _future = incRef(future); + } +} + +void +IcePy::GetConnectionAsyncCallback::response(const Ice::ConnectionPtr& conn) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) + { + // + // The future hasn't been set yet, which means the request is still being invoked. Save the results for later. + // + _connection = conn; + return; + } + + PyObjectHandle pyConn = createConnection(conn, _communicator); + PyObjectHandle tmp = callMethod(_future, "set_result", pyConn.get()); + PyErr_Clear(); + + Py_DECREF(_future); // Break cyclic dependency. + _future = 0; +} + +void +IcePy::GetConnectionAsyncCallback::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + if(!_future) + { + // + // The future hasn't been set yet, which means the request is still being invoked. Save the results for later. + // + _exception = convertException(ex); + return; + } + + PyObjectHandle exh = convertException(ex); + PyObjectHandle tmp = callMethod(_future, "set_exception", exh.get()); + PyErr_Clear(); + + Py_DECREF(_future); // Break cyclic dependency. + _future = 0; +} + // // ServantWrapper implementation. // @@ -4172,7 +4586,7 @@ IcePy::BlobjectServantWrapper::ice_invoke_async(const Ice::AMD_Object_ice_invoke try { - UpcallPtr up = new BlobjectUpcall(_amd, cb); + UpcallPtr up = new BlobjectUpcall(cb); up->dispatch(_servant, inParams, current); } catch(const Ice::Exception& ex) @@ -4199,3 +4613,50 @@ IcePy::createServantWrapper(PyObject* servant) return new TypedServantWrapper(servant); } + +PyObject* +IcePy::createFuture() +{ + PyObject* futureType = lookupType("Ice.Future"); + assert(futureType); + PyObjectHandle args = PyTuple_New(0); + if(!args.get()) + { + return 0; + } + PyTypeObject* type = reinterpret_cast<PyTypeObject*>(futureType); + PyObject* future = type->tp_new(type, args.get(), 0); + if(!future) + { + return 0; + } + type->tp_init(future, args.get(), 0); // Call the constructor + return future; +} + +PyObject* +IcePy::createFuture(const string& operation, PyObject* asyncResult) +{ + if(!asyncResult) // Can be nil for batch invocations. + { + asyncResult = Py_None; + } + + PyObject* futureType = lookupType("Ice.InvocationFuture"); + assert(futureType); + PyObjectHandle args = PyTuple_New(2); + if(!args.get()) + { + return 0; + } + PyTuple_SET_ITEM(args.get(), 0, createString(operation)); + PyTuple_SET_ITEM(args.get(), 1, incRef(asyncResult)); + PyTypeObject* type = reinterpret_cast<PyTypeObject*>(futureType); + PyObject* future = type->tp_new(type, args.get(), 0); + if(!future) + { + return 0; + } + type->tp_init(future, args.get(), 0); // Call the constructor + return future; +} diff --git a/python/modules/IcePy/Operation.h b/python/modules/IcePy/Operation.h index cc8736f1ec7..3d0c2e44850 100644 --- a/python/modules/IcePy/Operation.h +++ b/python/modules/IcePy/Operation.h @@ -15,6 +15,8 @@ #include <Ice/Object.h> #include <Ice/AsyncResultF.h> #include <Ice/CommunicatorF.h> +#include <IceUtil/Monitor.h> +#include <Util.h> namespace IcePy { @@ -25,6 +27,7 @@ bool initOperation(PyObject*); // Builtin operations. // PyObject* invokeBuiltin(PyObject*, const std::string&, PyObject*); +PyObject* invokeBuiltinAsync(PyObject*, const std::string&, PyObject*); PyObject* beginBuiltin(PyObject*, const std::string&, PyObject*); PyObject* endBuiltin(PyObject*, const std::string&, PyObject*); @@ -63,6 +66,31 @@ protected: typedef IceUtil::Handle<GetConnectionCallback> GetConnectionCallbackPtr; // +// Used as the callback for getConnectionAsync operation. +// +class GetConnectionAsyncCallback : public IceUtil::Shared +{ +public: + + GetConnectionAsyncCallback(const Ice::CommunicatorPtr&, const std::string&); + ~GetConnectionAsyncCallback(); + + void setFuture(PyObject*); + + void response(const Ice::ConnectionPtr&); + void exception(const Ice::Exception&); + +protected: + + Ice::CommunicatorPtr _communicator; + std::string _op; + PyObject* _future; + Ice::ConnectionPtr _connection; + PyObject* _exception; +}; +typedef IceUtil::Handle<GetConnectionAsyncCallback> GetConnectionAsyncCallbackPtr; + +// // Used as the callback for the various flushBatchRequest operations. // class FlushCallback : public IceUtil::Shared @@ -84,6 +112,31 @@ protected: typedef IceUtil::Handle<FlushCallback> FlushCallbackPtr; // +// Used as the callback for the various flushBatchRequestAsync operations. +// +class FlushAsyncCallback : public IceUtil::Shared +{ +public: + + FlushAsyncCallback(const std::string&); + ~FlushAsyncCallback(); + + void setFuture(PyObject*); + + void exception(const Ice::Exception&); + void sent(bool); + +protected: + + std::string _op; + PyObject* _future; + bool _sent; + bool _sentSynchronously; + PyObject* _exception; +}; +typedef IceUtil::Handle<FlushAsyncCallback> FlushAsyncCallbackPtr; + +// // ServantWrapper handles dispatching to a Python servant. // class ServantWrapper : public Ice::BlobjectArrayAsync @@ -103,6 +156,9 @@ typedef IceUtil::Handle<ServantWrapper> ServantWrapperPtr; ServantWrapperPtr createServantWrapper(PyObject*); +PyObject* createFuture(); +PyObject* createFuture(const std::string&, PyObject*); + } #endif diff --git a/python/modules/IcePy/Proxy.cpp b/python/modules/IcePy/Proxy.cpp index 0d1c11bb082..a9fb3b09360 100644 --- a/python/modules/IcePy/Proxy.cpp +++ b/python/modules/IcePy/Proxy.cpp @@ -201,6 +201,27 @@ proxyIceIsA(ProxyObject* self, PyObject* args) extern "C" #endif static PyObject* +proxyIceIsAAsync(ProxyObject* self, PyObject* args) +{ + PyObject* type; + PyObject* ctx = Py_None; + if(!PyArg_ParseTuple(args, STRCAST("O|O!"), &type, &PyDict_Type, &ctx)) + { + return 0; + } + + // + // We need to reformat the arguments to match what is used by the generated code: ((params...), ctx|None) + // + PyObjectHandle newArgs = Py_BuildValue(STRCAST("((O), O)"), type, ctx); + + return invokeBuiltinAsync(reinterpret_cast<PyObject*>(self), "ice_isA", newArgs.get()); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceIsA(ProxyObject* self, PyObject* args, PyObject* kwds) { static char* argNames[] = @@ -265,6 +286,26 @@ proxyIcePing(ProxyObject* self, PyObject* args) extern "C" #endif static PyObject* +proxyIcePingAsync(ProxyObject* self, PyObject* args) +{ + PyObject* ctx = Py_None; + if(!PyArg_ParseTuple(args, STRCAST("|O!"), &PyDict_Type, &ctx)) + { + return 0; + } + + // + // We need to reformat the arguments to match what is used by the generated code: ((params...), ctx|None) + // + PyObjectHandle newArgs = Py_BuildValue(STRCAST("((), O)"), ctx); + + return invokeBuiltinAsync(reinterpret_cast<PyObject*>(self), "ice_ping", newArgs.get()); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIcePing(ProxyObject* self, PyObject* args, PyObject* kwds) { static char* argNames[] = @@ -327,6 +368,26 @@ proxyIceIds(ProxyObject* self, PyObject* args) extern "C" #endif static PyObject* +proxyIceIdsAsync(ProxyObject* self, PyObject* args) +{ + PyObject* ctx = Py_None; + if(!PyArg_ParseTuple(args, STRCAST("|O!"), &PyDict_Type, &ctx)) + { + return 0; + } + + // + // We need to reformat the arguments to match what is used by the generated code: ((params...), ctx|None) + // + PyObjectHandle newArgs = Py_BuildValue(STRCAST("((), O)"), ctx); + + return invokeBuiltinAsync(reinterpret_cast<PyObject*>(self), "ice_ids", newArgs.get()); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceIds(ProxyObject* self, PyObject* args, PyObject* kwds) { static char* argNames[] = @@ -389,6 +450,26 @@ proxyIceId(ProxyObject* self, PyObject* args) extern "C" #endif static PyObject* +proxyIceIdAsync(ProxyObject* self, PyObject* args) +{ + PyObject* ctx = Py_None; + if(!PyArg_ParseTuple(args, STRCAST("|O!"), &PyDict_Type, &ctx)) + { + return 0; + } + + // + // We need to reformat the arguments to match what is used by the generated code: ((params...), ctx|None) + // + PyObjectHandle newArgs = Py_BuildValue(STRCAST("((), O)"), ctx); + + return invokeBuiltinAsync(reinterpret_cast<PyObject*>(self), "ice_id", newArgs.get()); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceId(ProxyObject* self, PyObject* args, PyObject* kwds) { static char* argNames[] = @@ -1733,6 +1814,51 @@ proxyIceGetConnection(ProxyObject* self) extern "C" #endif static PyObject* +proxyIceGetConnectionAsync(ProxyObject* self, PyObject* /*args*/, PyObject* /*kwds*/) +{ + assert(self->proxy); + const string op = "ice_getConnection"; + + GetConnectionAsyncCallbackPtr d = new GetConnectionAsyncCallback(*self->communicator, op); + Ice::Callback_Object_ice_getConnectionPtr cb = + Ice::newCallback_Object_ice_getConnection(d, &GetConnectionAsyncCallback::response, + &GetConnectionAsyncCallback::exception); + + Ice::AsyncResultPtr result; + + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + + result = (*self->proxy)->begin_ice_getConnection(cb); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + PyObjectHandle communicatorObj = getCommunicatorWrapper(*self->communicator); + PyObjectHandle asyncResultObj = + createAsyncResult(result, reinterpret_cast<PyObject*>(self), 0, communicatorObj.get()); + if(!asyncResultObj.get()) + { + return 0; + } + + PyObjectHandle future = createFuture(op, asyncResultObj.get()); + if(!future.get()) + { + return 0; + } + d->setFuture(future.get()); + return future.release(); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceGetConnection(ProxyObject* self, PyObject* args, PyObject* kwds) { assert(self->proxy); @@ -1894,6 +2020,50 @@ proxyIceFlushBatchRequests(ProxyObject* self) extern "C" #endif static PyObject* +proxyIceFlushBatchRequestsAsync(ProxyObject* self, PyObject* /*args*/, PyObject* /*kwds*/) +{ + assert(self->proxy); + const string op = "ice_flushBatchRequests"; + + FlushAsyncCallbackPtr d = new FlushAsyncCallback(op); + Ice::Callback_Object_ice_flushBatchRequestsPtr cb = + Ice::newCallback_Object_ice_flushBatchRequests(d, &FlushAsyncCallback::exception, &FlushAsyncCallback::sent); + + Ice::AsyncResultPtr result; + + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + + result = (*self->proxy)->begin_ice_flushBatchRequests(cb); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + PyObjectHandle communicatorObj = getCommunicatorWrapper(*self->communicator); + PyObjectHandle asyncResultObj = + createAsyncResult(result, reinterpret_cast<PyObject*>(self), 0, communicatorObj.get()); + if(!asyncResultObj.get()) + { + return 0; + } + + PyObjectHandle future = createFuture(op, asyncResultObj.get()); + if(!future.get()) + { + return 0; + } + d->setFuture(future.get()); + return future.release(); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceFlushBatchRequests(ProxyObject* self, PyObject* args, PyObject* kwds) { assert(self->proxy); @@ -2030,7 +2200,7 @@ AMI_Object_ice_flushBatchRequestsI::exception(const Ice::Exception& ex) ostringstream ostr; ostr << "AMI callback object for ice_flushBatchRequests does not define " << methodName << "()"; string str = ostr.str(); - PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + PyErr_WarnEx(PyExc_RuntimeWarning, const_cast<char*>(str.c_str()), 1); } else { @@ -2081,6 +2251,15 @@ proxyIceInvoke(ProxyObject* self, PyObject* args) extern "C" #endif static PyObject* +proxyIceInvokeAsync(ProxyObject* self, PyObject* args, PyObject* /*kwds*/) +{ + return iceInvokeAsync(reinterpret_cast<PyObject*>(self), args); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* proxyBeginIceInvoke(ProxyObject* self, PyObject* args, PyObject* kwds) { return beginIceInvoke(reinterpret_cast<PyObject*>(self), args, kwds); @@ -2389,24 +2568,32 @@ static PyMethodDef ProxyMethods[] = PyDoc_STR(STRCAST("ice_toString() -> string")) }, { STRCAST("ice_isA"), reinterpret_cast<PyCFunction>(proxyIceIsA), METH_VARARGS, PyDoc_STR(STRCAST("ice_isA(type, [ctx]) -> bool")) }, + { STRCAST("ice_isAAsync"), reinterpret_cast<PyCFunction>(proxyIceIsAAsync), METH_VARARGS, + PyDoc_STR(STRCAST("ice_isAAsync(type, [ctx]) -> Ice.Future")) }, { STRCAST("begin_ice_isA"), reinterpret_cast<PyCFunction>(proxyBeginIceIsA), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_isA(type[, _response][, _ex][, _sent][, _ctx]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_isA"), reinterpret_cast<PyCFunction>(proxyEndIceIsA), METH_VARARGS, PyDoc_STR(STRCAST("end_ice_isA(Ice.AsyncResult) -> bool")) }, { STRCAST("ice_ping"), reinterpret_cast<PyCFunction>(proxyIcePing), METH_VARARGS, PyDoc_STR(STRCAST("ice_ping([ctx]) -> None")) }, + { STRCAST("ice_pingAsync"), reinterpret_cast<PyCFunction>(proxyIcePingAsync), METH_VARARGS, + PyDoc_STR(STRCAST("ice_pingAsync([ctx]) -> Ice.Future")) }, { STRCAST("begin_ice_ping"), reinterpret_cast<PyCFunction>(proxyBeginIcePing), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_ping([_response][, _ex][, _sent][, _ctx]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_ping"), reinterpret_cast<PyCFunction>(proxyEndIcePing), METH_VARARGS, PyDoc_STR(STRCAST("end_ice_ping(Ice.AsyncResult) -> None")) }, { STRCAST("ice_ids"), reinterpret_cast<PyCFunction>(proxyIceIds), METH_VARARGS, PyDoc_STR(STRCAST("ice_ids([ctx]) -> list")) }, + { STRCAST("ice_idsAsync"), reinterpret_cast<PyCFunction>(proxyIceIdsAsync), METH_VARARGS, + PyDoc_STR(STRCAST("ice_idsAsync([ctx]) -> Ice.Future")) }, { STRCAST("begin_ice_ids"), reinterpret_cast<PyCFunction>(proxyBeginIceIds), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_ids([_response][, _ex][, _sent][, _ctx]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_ids"), reinterpret_cast<PyCFunction>(proxyEndIceIds), METH_VARARGS, PyDoc_STR(STRCAST("end_ice_ids(Ice.AsyncResult) -> list")) }, { STRCAST("ice_id"), reinterpret_cast<PyCFunction>(proxyIceId), METH_VARARGS, PyDoc_STR(STRCAST("ice_id([ctx]) -> string")) }, + { STRCAST("ice_idAsync"), reinterpret_cast<PyCFunction>(proxyIceIdAsync), METH_VARARGS, + PyDoc_STR(STRCAST("ice_idAsync([ctx]) -> Ice.Future")) }, { STRCAST("begin_ice_id"), reinterpret_cast<PyCFunction>(proxyBeginIceId), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_id([_response][, _ex][, _sent][, _ctx]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_id"), reinterpret_cast<PyCFunction>(proxyEndIceId), METH_VARARGS, @@ -2501,6 +2688,8 @@ static PyMethodDef ProxyMethods[] = PyDoc_STR(STRCAST("ice_connectionId(string) -> Ice.ObjectPrx")) }, { STRCAST("ice_getConnection"), reinterpret_cast<PyCFunction>(proxyIceGetConnection), METH_NOARGS, PyDoc_STR(STRCAST("ice_getConnection() -> Ice.Connection")) }, + { STRCAST("ice_getConnectionAsync"), reinterpret_cast<PyCFunction>(proxyIceGetConnectionAsync), + METH_NOARGS, PyDoc_STR(STRCAST("ice_getConnectionAsync() -> Ice.Future")) }, { STRCAST("begin_ice_getConnection"), reinterpret_cast<PyCFunction>(proxyBeginIceGetConnection), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_getConnection([_response][, _ex]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_getConnection"), reinterpret_cast<PyCFunction>(proxyEndIceGetConnection), METH_VARARGS, @@ -2509,12 +2698,16 @@ static PyMethodDef ProxyMethods[] = PyDoc_STR(STRCAST("ice_getCachedConnection() -> Ice.Connection")) }, { STRCAST("ice_flushBatchRequests"), reinterpret_cast<PyCFunction>(proxyIceFlushBatchRequests), METH_NOARGS, PyDoc_STR(STRCAST("ice_flushBatchRequests() -> void")) }, + { STRCAST("ice_flushBatchRequestsAsync"), reinterpret_cast<PyCFunction>(proxyIceFlushBatchRequestsAsync), + METH_NOARGS, PyDoc_STR(STRCAST("ice_flushBatchRequestsAsync() -> Ice.Future")) }, { STRCAST("begin_ice_flushBatchRequests"), reinterpret_cast<PyCFunction>(proxyBeginIceFlushBatchRequests), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_flushBatchRequests([_ex][, _sent]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_flushBatchRequests"), reinterpret_cast<PyCFunction>(proxyEndIceFlushBatchRequests), METH_VARARGS, PyDoc_STR(STRCAST("end_ice_flushBatchRequests(Ice.AsyncResult) -> void")) }, { STRCAST("ice_invoke"), reinterpret_cast<PyCFunction>(proxyIceInvoke), METH_VARARGS, PyDoc_STR(STRCAST("ice_invoke(operation, mode, inParams) -> bool, outParams")) }, + { STRCAST("ice_invokeAsync"), reinterpret_cast<PyCFunction>(proxyIceInvokeAsync), METH_VARARGS | METH_KEYWORDS, + PyDoc_STR(STRCAST("ice_invokeAsync(op, mode, inParams[, _ctx]) -> Ice.Future")) }, { STRCAST("begin_ice_invoke"), reinterpret_cast<PyCFunction>(proxyBeginIceInvoke), METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_ice_invoke(op, mode, inParams[, _response][, _ex][, _sent][, _ctx]) -> Ice.AsyncResult")) }, { STRCAST("end_ice_invoke"), reinterpret_cast<PyCFunction>(proxyEndIceInvoke), METH_VARARGS, diff --git a/python/modules/IcePy/Slice.cpp b/python/modules/IcePy/Slice.cpp index 1294a80194c..4581a97f45b 100644 --- a/python/modules/IcePy/Slice.cpp +++ b/python/modules/IcePy/Slice.cpp @@ -72,6 +72,7 @@ IcePy_loadSlice(PyObject* /*self*/, PyObject* args) opts.addOpt("", "underscore"); opts.addOpt("", "checksum"); opts.addOpt("", "all"); + opts.addOpt("", "python3"); vector<string> files; try @@ -102,6 +103,7 @@ IcePy_loadSlice(PyObject* /*self*/, PyObject* args) bool underscore = opts.isSet("underscore"); bool all = false; bool checksum = false; + bool python3 = false; if(opts.isSet("D")) { vector<string> optargs = opts.argVec("D"); @@ -129,6 +131,7 @@ IcePy_loadSlice(PyObject* /*self*/, PyObject* args) debug = opts.isSet("d") || opts.isSet("debug"); all = opts.isSet("all"); checksum = opts.isSet("checksum"); + python3 = opts.isSet("python3"); bool ignoreRedefs = false; bool keepComments = true; @@ -161,11 +164,13 @@ IcePy_loadSlice(PyObject* /*self*/, PyObject* args) ostringstream codeStream; IceUtilInternal::Output out(codeStream); out.setUseTab(false); + // - // Python magic comment to set the file encoding, it must be first or second line + // Emit a Python magic comment to set the file encoding. + // It must be the first or second line. // out << "# -*- coding: utf-8 -*-\n"; - generate(u, all, checksum, includePaths, out); + generate(u, all, checksum, python3, includePaths, out); u->destroy(); string code = codeStream.str(); diff --git a/python/modules/IcePy/Util.cpp b/python/modules/IcePy/Util.cpp index b40d9f3dcfb..8d0cd602aae 100644 --- a/python/modules/IcePy/Util.cpp +++ b/python/modules/IcePy/Util.cpp @@ -468,11 +468,12 @@ IcePy::PyException::getTraceback() PyObjectHandle str = createString("traceback"); PyObjectHandle mod = PyImport_Import(str.get()); assert(mod.get()); // Unable to import traceback module - Python installation error? - PyObject* d = PyModule_GetDict(mod.get()); - PyObject* func = PyDict_GetItemString(d, "format_exception"); + PyObject* func = PyDict_GetItemString(PyModule_GetDict(mod.get()), "format_exception"); assert(func); // traceback.format_exception must be present. PyObjectHandle args = Py_BuildValue("(OOO)", _type.get(), ex.get(), _tb.get()); + assert(args.get()); PyObjectHandle list = PyObject_CallObject(func, args.get()); + assert(list.get()); string result; for(Py_ssize_t i = 0; i < PyList_GET_SIZE(list.get()); ++i) @@ -1090,6 +1091,60 @@ IcePy::getEncodingVersion(PyObject* args, Ice::EncodingVersion& v) return true; } +PyObject* +IcePy::callMethod(PyObject* obj, const string& name, PyObject* arg1, PyObject* arg2) +{ + PyObjectHandle method = PyObject_GetAttrString(obj, const_cast<char*>(name.c_str())); + if(!method.get()) + { + return 0; + } + return callMethod(method.get(), arg1, arg2); +} + +PyObject* +IcePy::callMethod(PyObject* method, PyObject* arg1, PyObject* arg2) +{ + PyObjectHandle args; + if(arg1 && arg2) + { + args = PyTuple_New(2); + if(!args.get()) + { + return 0; + } + PyTuple_SET_ITEM(args.get(), 0, incRef(arg1)); + PyTuple_SET_ITEM(args.get(), 1, incRef(arg2)); + } + else if(arg1) + { + args = PyTuple_New(1); + if(!args.get()) + { + return 0; + } + PyTuple_SET_ITEM(args.get(), 0, incRef(arg1)); + } + else if(arg2) + { + args = PyTuple_New(1); + if(!args.get()) + { + return 0; + } + PyTuple_SET_ITEM(args.get(), 0, incRef(arg2)); + } + else + { + args = PyTuple_New(0); + if(!args.get()) + { + return 0; + } + } + return PyObject_Call(method, args.get(), 0); +} + extern "C" PyObject* IcePy_stringVersion(PyObject* /*self*/) diff --git a/python/modules/IcePy/Util.h b/python/modules/IcePy/Util.h index 54f1df6b21f..76035279ebe 100644 --- a/python/modules/IcePy/Util.h +++ b/python/modules/IcePy/Util.h @@ -28,6 +28,12 @@ namespace IcePy { +inline PyObject* incRef(PyObject* obj) +{ + Py_XINCREF(obj); + return obj; +} + // // This should be used instead of Py_False to avoid GCC compiler warnings. // @@ -58,16 +64,12 @@ inline PyObject* getTrue() inline PyObject* incFalse() { - PyObject* f = getFalse(); - Py_INCREF(f); - return f; + return incRef(getFalse()); } inline PyObject* incTrue() { - PyObject* t = getTrue(); - Py_INCREF(t); - return t; + return incRef(getTrue()); } // @@ -262,6 +264,12 @@ PyObject* createEncodingVersion(const Ice::EncodingVersion&); // bool getEncodingVersion(PyObject*, Ice::EncodingVersion&); +// +// Call a Python method. +// +PyObject* callMethod(PyObject*, const std::string&, PyObject* = 0, PyObject* = 0); +PyObject* callMethod(PyObject*, PyObject* = 0, PyObject* = 0); + } extern "C" PyObject* IcePy_stringVersion(PyObject*); |