diff options
author | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
---|---|---|
committer | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
commit | 630a37d2fe66f24518299e705f958b571803c522 (patch) | |
tree | 969723791bdc4d73bb099c19d45554d0ca241ad9 /python/modules/IcePy/Operation.cpp | |
parent | Fix some README.md markdown formatting (diff) | |
download | ice-630a37d2fe66f24518299e705f958b571803c522.tar.bz2 ice-630a37d2fe66f24518299e705f958b571803c522.tar.xz ice-630a37d2fe66f24518299e705f958b571803c522.zip |
py -> python
rb -> ruby
objc -> objective-c
cs -> csharp
Diffstat (limited to 'python/modules/IcePy/Operation.cpp')
-rw-r--r-- | python/modules/IcePy/Operation.cpp | 4219 |
1 files changed, 4219 insertions, 0 deletions
diff --git a/python/modules/IcePy/Operation.cpp b/python/modules/IcePy/Operation.cpp new file mode 100644 index 00000000000..5ca673898f4 --- /dev/null +++ b/python/modules/IcePy/Operation.cpp @@ -0,0 +1,4219 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifdef _WIN32 +# include <IceUtil/Config.h> +#endif +#include <Operation.h> +#include <Communicator.h> +#include <Current.h> +#include <Proxy.h> +#include <Thread.h> +#include <Types.h> +#include <Connection.h> +#include <Util.h> +#include <Ice/Communicator.h> +#include <Ice/IncomingAsync.h> +#include <Ice/Initialize.h> +#include <Ice/LocalException.h> +#include <Ice/Logger.h> +#include <Ice/ObjectAdapter.h> +#include <Ice/AsyncResult.h> +#include <Ice/Properties.h> +#include <Ice/Proxy.h> +#include <Slice/PythonUtil.h> + +using namespace std; +using namespace IcePy; +using namespace Slice::Python; + +namespace IcePy +{ + +// +// Information about an operation's parameter. +// +class ParamInfo : public UnmarshalCallback +{ +public: + + virtual void unmarshaled(PyObject*, PyObject*, void*); + + Ice::StringSeq metaData; + TypeInfoPtr type; + bool optional; + int tag; + int pos; +}; +typedef IceUtil::Handle<ParamInfo> ParamInfoPtr; +typedef list<ParamInfoPtr> ParamInfoList; + +// +// Encapsulates attributes of an operation. +// +class Operation : public IceUtil::Shared +{ +public: + + Operation(const char*, PyObject*, PyObject*, int, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*); + + void deprecate(const string&); + + string name; + Ice::OperationMode mode; + Ice::OperationMode sendMode; + bool amd; + Ice::FormatType format; + Ice::StringSeq metaData; + ParamInfoList inParams; + ParamInfoList optionalInParams; + ParamInfoList outParams; + ParamInfoList optionalOutParams; + ParamInfoPtr returnType; + ExceptionInfoList exceptions; + string dispatchName; + bool sendsClasses; + bool returnsClasses; + bool pseudoOp; + +private: + + string _deprecateMessage; + + static void convertParams(PyObject*, ParamInfoList&, int, bool&); + static ParamInfoPtr convertParam(PyObject*, int); +}; +typedef IceUtil::Handle<Operation> OperationPtr; + +// +// The base class for client-side invocations. +// +class Invocation : virtual public IceUtil::Shared +{ +public: + + Invocation(const Ice::ObjectPrx&); + + virtual PyObject* invoke(PyObject*, PyObject* = 0) = 0; + +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: + + TypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); + +protected: + + OperationPtr _op; + Ice::CommunicatorPtr _communicator; + + enum MappingType { SyncMapping, AsyncMapping, OldAsyncMapping }; + + bool prepareRequest(PyObject*, MappingType, Ice::OutputStreamPtr&, 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; +}; + +// +// Synchronous typed invocation. +// +class SyncTypedInvocation : virtual public TypedInvocation +{ +public: + + SyncTypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); + + virtual PyObject* invoke(PyObject*, PyObject* = 0); +}; + +// +// Asynchronous typed invocation. +// +class AsyncTypedInvocation : virtual public TypedInvocation +{ +public: + + AsyncTypedInvocation(const Ice::ObjectPrx&, PyObject*, const OperationPtr&); + ~AsyncTypedInvocation(); + + virtual PyObject* invoke(PyObject*, PyObject* = 0); + PyObject* end(const Ice::ObjectPrx&, const OperationPtr&, const Ice::AsyncResultPtr&); + + string opName() const; + + void response(bool, const pair<const Ice::Byte*, const Ice::Byte*>&); + void exception(const Ice::Exception&); + void sent(bool); + +protected: + + void checkAsyncTwowayOnly(const Ice::ObjectPrx&) const; + + PyObject* _pyProxy; + PyObject* _response; + PyObject* _ex; + PyObject* _sent; +}; +typedef IceUtil::Handle<AsyncTypedInvocation> AsyncTypedInvocationPtr; + +// +// Old-style asynchronous typed invocation. +// +class OldAsyncTypedInvocation : virtual public TypedInvocation +{ +public: + + OldAsyncTypedInvocation(const Ice::ObjectPrx&, const OperationPtr&); + ~OldAsyncTypedInvocation(); + + 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); + +protected: + + PyObject* _callback; +}; + +// +// Synchronous blobject invocation. +// +class SyncBlobjectInvocation : virtual public Invocation +{ +public: + + SyncBlobjectInvocation(const Ice::ObjectPrx&); + + virtual PyObject* invoke(PyObject*, PyObject* = 0); +}; + +// +// Asynchronous blobject invocation. +// +class AsyncBlobjectInvocation : virtual public Invocation +{ +public: + + AsyncBlobjectInvocation(const Ice::ObjectPrx&, PyObject*); + ~AsyncBlobjectInvocation(); + + virtual PyObject* invoke(PyObject*, PyObject* = 0); + PyObject* end(const Ice::ObjectPrx&, const Ice::AsyncResultPtr&); + + void response(bool, const pair<const Ice::Byte*, const Ice::Byte*>&); + void exception(const Ice::Exception&); + void sent(bool); + +protected: + + PyObject* _pyProxy; + string _op; + PyObject* _response; + PyObject* _ex; + PyObject* _sent; +}; +typedef IceUtil::Handle<AsyncBlobjectInvocation> AsyncBlobjectInvocationPtr; + +// +// Old-style asynchronous blobject invocation. +// +class OldAsyncBlobjectInvocation : virtual public Invocation +{ +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); + +protected: + + string _op; + PyObject* _callback; +}; + +// +// The base class for server-side upcalls. +// +class Upcall : virtual 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; +}; +typedef IceUtil::Handle<Upcall> UpcallPtr; + +// +// TypedInvocation uses the information in the given Operation to validate, marshal, and unmarshal +// parameters and exceptions. +// +class TypedUpcall : virtual 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&); + +private: + + bool validateException(PyObject*) const; + + OperationPtr _op; + Ice::AMD_Object_ice_invokePtr _callback; + Ice::CommunicatorPtr _communicator; + bool _finished; +}; + +// +// Upcall for blobject servants. +// +class BlobjectUpcall : virtual public Upcall +{ +public: + + BlobjectUpcall(bool, 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&); + +private: + + bool _amd; + Ice::AMD_Object_ice_invokePtr _callback; + bool _finished; +}; + +// +// TypedServantWrapper uses the information in Operation to validate, marshal, and unmarshal +// parameters and exceptions. +// +class TypedServantWrapper : public ServantWrapper +{ +public: + + TypedServantWrapper(PyObject*); + + virtual void ice_invoke_async(const Ice::AMD_Object_ice_invokePtr&, + const pair<const Ice::Byte*, const Ice::Byte*>&, + const Ice::Current&); + +private: + + typedef map<string, OperationPtr> OperationMap; + OperationMap _operationMap; + OperationMap::iterator _lastOp; +}; + +// +// Encapsulates a blobject servant. +// +class BlobjectServantWrapper : public ServantWrapper +{ +public: + + BlobjectServantWrapper(PyObject*, bool); + + virtual void ice_invoke_async(const Ice::AMD_Object_ice_invokePtr&, + const pair<const Ice::Byte*, const Ice::Byte*>&, + const Ice::Current&); + +private: + + bool _amd; +}; + +struct OperationObject +{ + PyObject_HEAD + OperationPtr* op; +}; + +struct AMDCallbackObject +{ + PyObject_HEAD + UpcallPtr* upcall; + Ice::EncodingVersion encoding; +}; + +struct AsyncResultObject +{ + PyObject_HEAD + Ice::AsyncResultPtr* result; + InvocationPtr* invocation; + PyObject* proxy; + PyObject* connection; + PyObject* communicator; +}; + +extern PyTypeObject OperationType; +extern PyTypeObject AMDCallbackType; + +class UserExceptionReaderFactoryI : public Ice::UserExceptionReaderFactory +{ +public: + + UserExceptionReaderFactoryI(const Ice::CommunicatorPtr& communicator) : + _communicator(communicator) + { + } + + virtual void createAndThrow(const string& id) const + { + ExceptionInfoPtr info = lookupExceptionInfo(id); + if(info) + { + throw ExceptionReader(_communicator, info); + } + } + +private: + + const Ice::CommunicatorPtr _communicator; +}; + +} + +namespace +{ + +OperationPtr +getOperation(PyObject* p) +{ + assert(PyObject_IsInstance(p, reinterpret_cast<PyObject*>(&OperationType)) == 1); + OperationObject* obj = reinterpret_cast<OperationObject*>(p); + return *obj->op; +} + +void +handleException() +{ + assert(PyErr_Occurred()); + + PyException ex; // Retrieve it before another Python API call clears it. + + // + // A callback that calls sys.exit() will raise the SystemExit exception. + // This is normally caught by the interpreter, causing it to exit. + // However, we have no way to pass this exception to the interpreter, + // so we act on it directly. + // + ex.checkSystemExit(); + + ex.raise(); +} + +void +callException(PyObject* method, PyObject* ex) +{ + PyObjectHandle args = Py_BuildValue(STRCAST("(O)"), ex); + PyObjectHandle tmp = PyObject_Call(method, args.get(), 0); + if(PyErr_Occurred()) + { + handleException(); // Callback raised an exception. + } +} + +void +callException(PyObject* method, const Ice::Exception& ex) +{ + PyObjectHandle exh = convertException(ex); + assert(exh.get()); + callException(method, exh.get()); +} + +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; + if(passArg) + { + args = Py_BuildValue(STRCAST("(O)"), sentSynchronously ? getTrue() : getFalse()); + } + else + { + args = PyTuple_New(0); + } + PyObjectHandle tmp = PyObject_Call(method, args.get(), 0); + 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 +extern "C" +#endif +static OperationObject* +operationNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) +{ + OperationObject* self = reinterpret_cast<OperationObject*>(type->tp_alloc(type, 0)); + if(!self) + { + return 0; + } + self->op = 0; + return self; +} + +#ifdef WIN32 +extern "C" +#endif +static int +operationInit(OperationObject* self, PyObject* args, PyObject* /*kwds*/) +{ + char* name; + PyObject* modeType = lookupType("Ice.OperationMode"); + assert(modeType); + PyObject* mode; + PyObject* sendMode; + int amd; + PyObject* format; + PyObject* metaData; + PyObject* inParams; + PyObject* outParams; + PyObject* returnType; + PyObject* exceptions; + if(!PyArg_ParseTuple(args, STRCAST("sO!O!iOO!O!O!OO!"), &name, modeType, &mode, modeType, &sendMode, &amd, + &format, &PyTuple_Type, &metaData, &PyTuple_Type, &inParams, &PyTuple_Type, &outParams, + &returnType, &PyTuple_Type, &exceptions)) + { + return -1; + } + + OperationPtr op = new Operation(name, mode, sendMode, amd, format, metaData, inParams, outParams, returnType, + exceptions); + self->op = new OperationPtr(op); + + return 0; +} + +#ifdef WIN32 +extern "C" +#endif +static void +operationDealloc(OperationObject* self) +{ + delete self->op; + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +operationInvoke(OperationObject* self, PyObject* args) +{ + PyObject* pyProxy; + PyObject* opArgs; + if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &pyProxy, &PyTuple_Type, &opArgs)) + { + return 0; + } + + Ice::ObjectPrx prx = getProxy(pyProxy); + assert(self->op); + + InvocationPtr i = new SyncTypedInvocation(prx, *self->op); + return i->invoke(opArgs); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +operationInvokeAsync(OperationObject* self, PyObject* args) +{ + PyObject* pyProxy; + PyObject* opArgs; + if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &pyProxy, &PyTuple_Type, &opArgs)) + { + return 0; + } + + Ice::ObjectPrx prx = getProxy(pyProxy); + assert(self->op); + + InvocationPtr i = new OldAsyncTypedInvocation(prx, *self->op); + return i->invoke(opArgs); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +operationBegin(OperationObject* self, PyObject* args) +{ + PyObject* proxy; + PyObject* opArgs; + if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &proxy, &PyTuple_Type, &opArgs)) + { + return 0; + } + + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new AsyncTypedInvocation(p, proxy, *self->op); + return i->invoke(opArgs); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +operationEnd(OperationObject* self, PyObject* args) +{ + PyObject* proxy; + PyObject* result; + if(!PyArg_ParseTuple(args, STRCAST("O!O!"), &ProxyType, &proxy, &AsyncResultType, &result)) + { + return 0; + } + + AsyncResultObject* ar = reinterpret_cast<AsyncResultObject*>(result); + assert(ar); + AsyncTypedInvocationPtr i = AsyncTypedInvocationPtr::dynamicCast(*ar->invocation); + if(!i) + { + PyErr_Format(PyExc_ValueError, STRCAST("invalid AsyncResult object passed to end_%s"), + (*self->op)->name.c_str()); + return 0; + } + Ice::ObjectPrx p = getProxy(proxy); + return i->end(p, *self->op, *ar->result); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +operationDeprecate(OperationObject* self, PyObject* args) +{ + char* msg; + if(!PyArg_ParseTuple(args, STRCAST("s"), &msg)) + { + return 0; + } + + assert(self->op); + (*self->op)->deprecate(msg); + + Py_INCREF(Py_None); + return Py_None; +} + +// +// AMDCallback operations +// + +#ifdef WIN32 +extern "C" +#endif +static AMDCallbackObject* +amdCallbackNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) +{ + AMDCallbackObject* self = reinterpret_cast<AMDCallbackObject*>(type->tp_alloc(type, 0)); + if(!self) + { + return 0; + } + self->upcall = 0; + return self; +} + +#ifdef WIN32 +extern "C" +#endif +static void +amdCallbackDealloc(AMDCallbackObject* self) +{ + delete self->upcall; + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +amdCallbackIceResponse(AMDCallbackObject* self, PyObject* args) +{ + try + { + assert(self->upcall); + (*self->upcall)->response(args, self->encoding); + } + catch(...) + { + // + // No exceptions should propagate to Python. + // + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +amdCallbackIceException(AMDCallbackObject* self, PyObject* args) +{ + PyObject* ex; + if(!PyArg_ParseTuple(args, STRCAST("O"), &ex)) + { + return 0; + } + + if(!PyObject_IsInstance(ex, PyExc_Exception)) + { + 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); + } + catch(...) + { + // + // No exceptions should propagate to Python. + // + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +// +// AsyncResult operations +// + +#ifdef WIN32 +extern "C" +#endif +static AsyncResultObject* +asyncResultNew(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwds*/) +{ + AsyncResultObject* self = reinterpret_cast<AsyncResultObject*>(type->tp_alloc(type, 0)); + if(!self) + { + return 0; + } + self->result = 0; + self->invocation = 0; + self->proxy = 0; + self->connection = 0; + self->communicator = 0; + return self; +} + +#ifdef WIN32 +extern "C" +#endif +static void +asyncResultDealloc(AsyncResultObject* self) +{ + delete self->result; + delete self->invocation; + Py_XDECREF(self->proxy); + Py_XDECREF(self->connection); + Py_XDECREF(self->communicator); + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultGetCommunicator(AsyncResultObject* self) +{ + if(self->communicator) + { + Py_INCREF(self->communicator); + return self->communicator; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultCancel(AsyncResultObject* self) +{ + try + { + (*self->result)->cancel(); + } + catch(...) + { + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultGetConnection(AsyncResultObject* self) +{ + if(self->connection) + { + Py_INCREF(self->connection); + return self->connection; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultGetProxy(AsyncResultObject* self) +{ + if(self->proxy) + { + Py_INCREF(self->proxy); + return self->proxy; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultIsCompleted(AsyncResultObject* self) +{ + bool b = false; + + try + { + assert(self->result); + b = (*self->result)->isCompleted(); + } + catch(...) + { + assert(false); + } + + PyRETURN_BOOL(b); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultWaitForCompleted(AsyncResultObject* self) +{ + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + try + { + assert(self->result); + (*self->result)->waitForCompleted(); + } + catch(...) + { + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultIsSent(AsyncResultObject* self) +{ + bool b = false; + + try + { + assert(self->result); + b = (*self->result)->isSent(); + } + catch(...) + { + assert(false); + } + + PyRETURN_BOOL(b); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultWaitForSent(AsyncResultObject* self) +{ + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + try + { + assert(self->result); + (*self->result)->waitForSent(); + } + catch(...) + { + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultThrowLocalException(AsyncResultObject* self) +{ + try + { + assert(self->result); + (*self->result)->throwLocalException(); + } + catch(const Ice::LocalException& ex) + { + setPythonException(ex); + return 0; + } + catch(...) + { + assert(false); + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultSentSynchronously(AsyncResultObject* self) +{ + bool b = false; + + try + { + assert(self->result); + b = (*self->result)->sentSynchronously(); + } + catch(...) + { + assert(false); + } + + PyRETURN_BOOL(b); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +asyncResultGetOperation(AsyncResultObject* self) +{ + string op; + + try + { + // + // Since the extension uses the Blobject API, calling (*self->result)->getOperation() + // always returns "ice_invoke" as the operation name. If the caller used a regular + // (typed) proxy method, we obtain the actual operation name from the invocation. + // + if(self->invocation) + { + AsyncTypedInvocationPtr i = AsyncTypedInvocationPtr::dynamicCast(*self->invocation); + if(i) + { + op = i->opName(); + } + } + if(op.empty()) + { + assert(self->result); + op = (*self->result)->getOperation(); + } + } + catch(...) + { + assert(false); + } + + return createString(op); +} + +// +// ParamInfo implementation. +// +void +IcePy::ParamInfo::unmarshaled(PyObject* val, PyObject* target, void* closure) +{ + assert(PyTuple_Check(target)); + long i = reinterpret_cast<long>(closure); + PyTuple_SET_ITEM(target, i, val); + Py_INCREF(val); // PyTuple_SET_ITEM steals a reference. +} + +// +// Operation implementation. +// +IcePy::Operation::Operation(const char* n, PyObject* m, PyObject* sm, int amdFlag, PyObject* fmt, PyObject* meta, + PyObject* in, PyObject* out, PyObject* ret, PyObject* ex) +{ + name = n; + + // + // mode + // + PyObjectHandle modeValue = PyObject_GetAttrString(m, STRCAST("value")); + mode = (Ice::OperationMode)static_cast<int>(PyLong_AsLong(modeValue.get())); + assert(!PyErr_Occurred()); + + // + // sendMode + // + PyObjectHandle sendModeValue = PyObject_GetAttrString(sm, STRCAST("value")); + sendMode = (Ice::OperationMode)static_cast<int>(PyLong_AsLong(sendModeValue.get())); + assert(!PyErr_Occurred()); + + // + // amd + // + amd = amdFlag ? true : false; + if(amd) + { + dispatchName = name + "_async"; + } + else + { + dispatchName = fixIdent(name); + } + + // + // format + // + if(fmt == Py_None) + { + format = Ice::DefaultFormat; + } + else + { + PyObjectHandle formatValue = PyObject_GetAttrString(fmt, STRCAST("value")); + format = (Ice::FormatType)static_cast<int>(PyLong_AsLong(formatValue.get())); + assert(!PyErr_Occurred()); + } + + // + // metaData + // + assert(PyTuple_Check(meta)); +#ifndef NDEBUG + bool b = +#endif + tupleToStringSeq(meta, metaData); + assert(b); + + // + // returnType + // + returnsClasses = false; + if(ret != Py_None) + { + returnType = convertParam(ret, 0); + if(!returnType->optional) + { + returnsClasses = returnType->type->usesClasses(); + } + } + + // + // inParams + // + sendsClasses = false; + convertParams(in, inParams, 0, sendsClasses); + + // + // outParams + // + convertParams(out, outParams, returnType ? 1 : 0, returnsClasses); + + class SortFn + { + public: + static bool compare(const ParamInfoPtr& lhs, const ParamInfoPtr& rhs) + { + return lhs->tag < rhs->tag; + } + + static bool isRequired(const ParamInfoPtr& i) + { + return !i->optional; + } + }; + + // + // The inParams list represents the parameters in the order of declaration. + // We also need a sorted list of optional parameters. + // + ParamInfoList l = inParams; + copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(optionalInParams)); + optionalInParams.sort(SortFn::compare); + + // + // The outParams list represents the parameters in the order of declaration. + // We also need a sorted list of optional parameters. If the return value is + // optional, we must include it in this list. + // + l = outParams; + copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(optionalOutParams)); + if(returnType && returnType->optional) + { + optionalOutParams.push_back(returnType); + } + optionalOutParams.sort(SortFn::compare); + + // + // exceptions + // + Py_ssize_t sz = PyTuple_GET_SIZE(ex); + for(Py_ssize_t i = 0; i < sz; ++i) + { + exceptions.push_back(getException(PyTuple_GET_ITEM(ex, i))); + } + + // + // Does the operation name start with "ice_"? + // + pseudoOp = name.find("ice_") == 0; +} + +void +IcePy::Operation::deprecate(const string& msg) +{ + if(!msg.empty()) + { + _deprecateMessage = msg; + } + else + { + _deprecateMessage = "operation " + name + " is deprecated"; + } +} + +void +IcePy::Operation::convertParams(PyObject* p, ParamInfoList& params, int posOffset, bool& usesClasses) +{ + int sz = static_cast<int>(PyTuple_GET_SIZE(p)); + for(int i = 0; i < sz; ++i) + { + PyObject* item = PyTuple_GET_ITEM(p, i); + ParamInfoPtr param = convertParam(item, i + posOffset); + params.push_back(param); + if(!param->optional && !usesClasses) + { + usesClasses = param->type->usesClasses(); + } + } +} + +ParamInfoPtr +IcePy::Operation::convertParam(PyObject* p, int pos) +{ + assert(PyTuple_Check(p)); + assert(PyTuple_GET_SIZE(p) == 4); + + ParamInfoPtr param = new ParamInfo; + + // + // metaData + // + PyObject* meta = PyTuple_GET_ITEM(p, 0); + assert(PyTuple_Check(meta)); +#ifndef NDEBUG + bool b = +#endif + tupleToStringSeq(meta, param->metaData); + assert(b); + + // + // type + // + PyObject* type = PyTuple_GET_ITEM(p, 1); + if(type != Py_None) + { + param->type = getType(type); + } + + // + // optional + // + param->optional = PyObject_IsTrue(PyTuple_GET_ITEM(p, 2)) == 1; + + // + // tag + // + param->tag = static_cast<int>(PyLong_AsLong(PyTuple_GET_ITEM(p, 3))); + + // + // position + // + param->pos = pos; + + return param; +} + +static PyMethodDef OperationMethods[] = +{ + { STRCAST("invoke"), reinterpret_cast<PyCFunction>(operationInvoke), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { STRCAST("invokeAsync"), reinterpret_cast<PyCFunction>(operationInvokeAsync), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { STRCAST("begin"), reinterpret_cast<PyCFunction>(operationBegin), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { STRCAST("end"), reinterpret_cast<PyCFunction>(operationEnd), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { STRCAST("deprecate"), reinterpret_cast<PyCFunction>(operationDeprecate), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { 0, 0 } /* sentinel */ +}; + +static PyMethodDef AMDCallbackMethods[] = +{ + { STRCAST("ice_response"), reinterpret_cast<PyCFunction>(amdCallbackIceResponse), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { STRCAST("ice_exception"), reinterpret_cast<PyCFunction>(amdCallbackIceException), METH_VARARGS, + PyDoc_STR(STRCAST("internal function")) }, + { 0, 0 } /* sentinel */ +}; + +static PyMethodDef AsyncResultMethods[] = +{ + { STRCAST("cancel"), reinterpret_cast<PyCFunction>(asyncResultCancel), METH_NOARGS, + PyDoc_STR(STRCAST("cancels the invocation")) }, + { STRCAST("getCommunicator"), reinterpret_cast<PyCFunction>(asyncResultGetCommunicator), METH_NOARGS, + PyDoc_STR(STRCAST("returns the communicator for the invocation")) }, + { STRCAST("getConnection"), reinterpret_cast<PyCFunction>(asyncResultGetConnection), METH_NOARGS, + PyDoc_STR(STRCAST("returns the connection for the invocation")) }, + { STRCAST("getProxy"), reinterpret_cast<PyCFunction>(asyncResultGetProxy), METH_NOARGS, + PyDoc_STR(STRCAST("returns the proxy for the invocation")) }, + { STRCAST("isCompleted"), reinterpret_cast<PyCFunction>(asyncResultIsCompleted), METH_NOARGS, + PyDoc_STR(STRCAST("returns true if the request is complete")) }, + { STRCAST("waitForCompleted"), reinterpret_cast<PyCFunction>(asyncResultWaitForCompleted), METH_NOARGS, + PyDoc_STR(STRCAST("blocks until the request is complete")) }, + { STRCAST("isSent"), reinterpret_cast<PyCFunction>(asyncResultIsSent), METH_NOARGS, + PyDoc_STR(STRCAST("returns true if the request is sent")) }, + { STRCAST("waitForSent"), reinterpret_cast<PyCFunction>(asyncResultWaitForSent), METH_NOARGS, + PyDoc_STR(STRCAST("blocks until the request is sent")) }, + { STRCAST("throwLocalException"), reinterpret_cast<PyCFunction>(asyncResultThrowLocalException), METH_NOARGS, + PyDoc_STR(STRCAST("throw location exception if the request failed with a local exception")) }, + { STRCAST("sentSynchronously"), reinterpret_cast<PyCFunction>(asyncResultSentSynchronously), METH_NOARGS, + 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")) }, + { 0, 0 } /* sentinel */ +}; + +namespace IcePy +{ + +PyTypeObject OperationType = +{ + /* 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.Operation"), /* tp_name */ + sizeof(OperationObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + reinterpret_cast<destructor>(operationDealloc), /* 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 */ + OperationMethods, /* 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 */ + reinterpret_cast<initproc>(operationInit), /* tp_init */ + 0, /* tp_alloc */ + reinterpret_cast<newfunc>(operationNew), /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +PyTypeObject AMDCallbackType = +{ + /* 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 */ + 0, /* tp_itemsize */ + /* methods */ + reinterpret_cast<destructor>(amdCallbackDealloc), /* 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 */ + AMDCallbackMethods, /* 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>(amdCallbackNew), /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +PyTypeObject AsyncResultType = +{ + /* 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.AsyncResult"), /* tp_name */ + sizeof(AsyncResultObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + reinterpret_cast<destructor>(asyncResultDealloc), /* 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 */ + AsyncResultMethods, /* 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>(asyncResultNew), /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; + +} + +bool +IcePy::initOperation(PyObject* module) +{ + if(PyType_Ready(&OperationType) < 0) + { + return false; + } + PyTypeObject* opType = &OperationType; // Necessary to prevent GCC's strict-alias warnings. + if(PyModule_AddObject(module, STRCAST("Operation"), reinterpret_cast<PyObject*>(opType)) < 0) + { + return false; + } + + if(PyType_Ready(&AMDCallbackType) < 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) + { + return false; + } + + if(PyType_Ready(&AsyncResultType) < 0) + { + return false; + } + PyTypeObject* arType = &AsyncResultType; // Necessary to prevent GCC's strict-alias warnings. + if(PyModule_AddObject(module, STRCAST("AsyncResult"), reinterpret_cast<PyObject*>(arType)) < 0) + { + return false; + } + + return true; +} + +// +// 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()) +{ +} + +bool +IcePy::TypedInvocation::prepareRequest(PyObject* args, MappingType mapping, Ice::OutputStreamPtr& os, + pair<const Ice::Byte*, const Ice::Byte*>& params) +{ + assert(PyTuple_Check(args)); + params.first = params.second = static_cast<const Ice::Byte*>(0); + + // + // 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()); + if(argc != paramCount) + { + string opName; + if(mapping == OldAsyncMapping) + { + opName = _op->name + "_async"; + } + else if(mapping == AsyncMapping) + { + opName = "begin_" + _op->name; + } + else + { + 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()) + { + try + { + // + // Marshal the in parameters. + // + os = Ice::createOutputStream(_communicator); + os->startEncapsulation(_prx->ice_getEncodingVersion(), _op->format); + + ObjectMap objectMap; + ParamInfoList::iterator p; + + // + // Validate the supplied arguments. + // + 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) + { + name = _op->name + "_async"; + } + else if(mapping == AsyncMapping) + { + name = "begin_" + _op->name; + } + else + { + 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())); + return false; + } + } + + // + // Marshal the required parameters. + // + for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p) + { + ParamInfoPtr info = *p; + if(!info->optional) + { + PyObject* arg = PyTuple_GET_ITEM(args, info->pos); + info->type->marshal(arg, os, &objectMap, false, &info->metaData); + } + } + + // + // Marshal the optional parameters. + // + for(p = _op->optionalInParams.begin(); p != _op->optionalInParams.end(); ++p) + { + ParamInfoPtr info = *p; + PyObject* arg = PyTuple_GET_ITEM(args, info->pos); + if(arg != Unset && os->writeOptional(info->tag, info->type->optionalFormat())) + { + info->type->marshal(arg, os, &objectMap, true, &info->metaData); + } + } + + if(_op->sendsClasses) + { + os->writePendingObjects(); + } + + os->endEncapsulation(); + params = os->finished(); + } + catch(const AbortMarshaling&) + { + assert(PyErr_Occurred()); + return false; + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return false; + } + } + + return true; +} + +PyObject* +IcePy::TypedInvocation::unmarshalResults(const pair<const Ice::Byte*, const Ice::Byte*>& bytes) +{ + Py_ssize_t numResults = static_cast<Py_ssize_t>(_op->outParams.size()); + if(_op->returnType) + { + numResults++; + } + + PyObjectHandle results = PyTuple_New(numResults); + if(results.get() && numResults > 0) + { + Ice::InputStreamPtr is = Ice::wrapInputStream(_communicator, bytes); + + // + // Store a pointer to a local SlicedDataUtil object as the stream's closure. + // This is necessary to support object unmarshaling (see ObjectReader). + // + SlicedDataUtil util; + assert(!is->closure()); + is->closure(&util); + + is->startEncapsulation(); + + ParamInfoList::iterator p; + + // + // Unmarshal the required out parameters. + // + for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p) + { + ParamInfoPtr info = *p; + if(!info->optional) + { + void* closure = reinterpret_cast<void*>(info->pos); + info->type->unmarshal(is, info, results.get(), closure, false, &info->metaData); + } + } + + // + // Unmarshal the required return value, if any. + // + if(_op->returnType && !_op->returnType->optional) + { + assert(_op->returnType->pos == 0); + void* closure = reinterpret_cast<void*>(_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) + { + ParamInfoPtr info = *p; + if(is->readOptional(info->tag, info->type->optionalFormat())) + { + void* closure = reinterpret_cast<void*>(info->pos); + info->type->unmarshal(is, info, results.get(), closure, true, &info->metaData); + } + else + { + if(PyTuple_SET_ITEM(results.get(), info->pos, Unset) < 0) + { + return 0; + } + Py_INCREF(Unset); // PyTuple_SET_ITEM steals a reference. + } + } + + if(_op->returnsClasses) + { + is->readPendingObjects(); + } + + is->endEncapsulation(); + + util.update(); + } + + return results.release(); +} + +PyObject* +IcePy::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ice::Byte*>& bytes) +{ + Ice::InputStreamPtr is = Ice::wrapInputStream(_communicator, bytes); + + // + // Store a pointer to a local SlicedDataUtil object as the stream's closure. + // This is necessary to support object unmarshaling (see ObjectReader). + // + SlicedDataUtil util; + assert(!is->closure()); + is->closure(&util); + + is->startEncapsulation(); + + try + { + Ice::UserExceptionReaderFactoryPtr factory = new UserExceptionReaderFactoryI(_communicator); + is->throwException(factory); + } + catch(const ExceptionReader& r) + { + is->endEncapsulation(); + + PyObject* ex = r.getException(); + + if(validateException(ex)) + { + util.update(); + + Ice::SlicedDataPtr slicedData = r.getSlicedData(); + if(slicedData) + { + SlicedDataUtil::setMember(ex, slicedData); + } + + Py_INCREF(ex); + return ex; + } + else + { + PyException pye(ex); // No traceback information available. + pye.raise(); + } + } + + throw Ice::UnknownUserException(__FILE__, __LINE__, "unknown exception"); +#ifdef __SUNPRO_CC + return 0; +#endif +} + +bool +IcePy::TypedInvocation::validateException(PyObject* ex) const +{ + for(ExceptionInfoList::const_iterator p = _op->exceptions.begin(); p != _op->exceptions.end(); ++p) + { + if(PyObject_IsInstance(ex, (*p)->pythonType.get())) + { + return true; + } + } + + return false; +} + +void +IcePy::TypedInvocation::checkTwowayOnly(const Ice::ObjectPrx& proxy) const +{ + if((_op->returnType != 0 || !_op->outParams.empty() || !_op->exceptions.empty()) && !proxy->ice_isTwoway()) + { + Ice::TwowayOnlyException ex(__FILE__, __LINE__); + ex.operation = _op->name; + throw ex; + } +} + +// +// SyncTypedInvocation +// +IcePy::SyncTypedInvocation::SyncTypedInvocation(const Ice::ObjectPrx& prx, const OperationPtr& op) : + Invocation(prx), TypedInvocation(prx, op) +{ +} + +PyObject* +IcePy::SyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) +{ + 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::OutputStreamPtr os; + pair<const Ice::Byte*, const Ice::Byte*> params; + if(!prepareRequest(pyparams, SyncMapping, os, params)) + { + return 0; + } + + try + { + checkTwowayOnly(_prx); + + // + // Invoke the operation. + // + vector<Ice::Byte> result; + bool status; + { + 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. + status = _prx->ice_invoke(_op->name, _op->sendMode, params, result, ctx); + } + else + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + status = _prx->ice_invoke(_op->name, _op->sendMode, params, result); + } + } + + // + // Process the reply. + // + if(_prx->ice_isTwoway()) + { + if(!status) + { + // + // Unmarshal a user exception. + // + pair<const Ice::Byte*, const Ice::Byte*> rb(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(!result.empty()) + { + rb.first = &result[0]; + rb.second = &result[0] + result.size(); + } + PyObjectHandle ex = unmarshalException(rb); + + // + // Set the Python exception. + // + setPythonException(ex.get()); + return 0; + } + else if(_op->outParams.size() > 0 || _op->returnType) + { + // + // Unmarshal the results. If there is more than one value to be returned, then return them + // in a tuple of the form (result, outParam1, ...). Otherwise just return the value. + // + pair<const Ice::Byte*, const Ice::Byte*> rb(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(!result.empty()) + { + rb.first = &result[0]; + rb.second = &result[0] + result.size(); + } + PyObjectHandle results = unmarshalResults(rb); + if(!results.get()) + { + return 0; + } + + if(PyTuple_GET_SIZE(results.get()) > 1) + { + return results.release(); + } + else + { + PyObject* ret = PyTuple_GET_ITEM(results.get(), 0); + if(!ret) + { + return 0; + } + else + { + Py_INCREF(ret); + return ret; + } + } + } + } + } + catch(const AbortMarshaling&) + { + assert(PyErr_Occurred()); + return 0; + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + Py_INCREF(Py_None); + return Py_None; +} + +// +// AsyncTypedInvocation +// +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) +{ + Py_INCREF(_pyProxy); +} + +IcePy::AsyncTypedInvocation::~AsyncTypedInvocation() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_pyProxy); + Py_XDECREF(_response); + Py_XDECREF(_ex); + Py_XDECREF(_sent); +} + +PyObject* +IcePy::AsyncTypedInvocation::invoke(PyObject* args, PyObject* /* kwds */) +{ + assert(PyTuple_Check(args)); + assert(PyTuple_GET_SIZE(args) == 5); // Format is ((params...), response|None, exception|None, sent|None, ctx|None) + PyObject* pyparams = PyTuple_GET_ITEM(args, 0); + assert(PyTuple_Check(pyparams)); + + PyObject* callable; + + callable = PyTuple_GET_ITEM(args, 1); + if(PyCallable_Check(callable)) + { + _response = callable; + Py_INCREF(_response); + } + else if(callable != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("response callback must be a callable object or None")); + return 0; + } + + callable = PyTuple_GET_ITEM(args, 2); + if(PyCallable_Check(callable)) + { + _ex = callable; + Py_INCREF(_ex); + } + else if(callable != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("exception callback must be a callable object or None")); + return 0; + } + + callable = PyTuple_GET_ITEM(args, 3); + if(PyCallable_Check(callable)) + { + _sent = callable; + Py_INCREF(_sent); + } + else if(callable != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("sent callback must be a callable object or None")); + return 0; + } + + if(!_ex && (_response || _sent)) + { + PyErr_Format(PyExc_RuntimeError, + STRCAST("exception callback must also be provided when response or sent callbacks are used")); + return 0; + } + + PyObject* pyctx = PyTuple_GET_ITEM(args, 4); + if(pyctx != Py_None && !PyDict_Check(pyctx)) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("context must be a dictionary or None")); + return 0; + } + + // + // Marshal the input parameters to a byte sequence. + // + Ice::OutputStreamPtr os; + pair<const Ice::Byte*, const Ice::Byte*> params; + if(!prepareRequest(pyparams, AsyncMapping, os, params)) + { + return 0; + } + + Ice::AsyncResultPtr result; + try + { + checkAsyncTwowayOnly(_prx); + + Ice::Callback_Object_ice_invokePtr cb; + if(_response || _ex || _sent) + { + cb = Ice::newCallback_Object_ice_invoke(this, &AsyncTypedInvocation::response, + &AsyncTypedInvocation::exception, &AsyncTypedInvocation::sent); + } + + // + // Invoke the operation asynchronously. + // + if(pyctx != Py_None) + { + Ice::Context ctx; + if(!dictionaryToContext(pyctx, ctx)) + { + return 0; + } + + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + if(cb) + { + result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params, ctx, cb); + } + else + { + result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params, ctx); + } + } + else + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + if(cb) + { + result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params, cb); + } + else + { + result = _prx->begin_ice_invoke(_op->name, _op->sendMode, params); + } + } + } + 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); + } + + assert(result); + AsyncResultObject* obj = asyncResultNew(&AsyncResultType, 0, 0); + if(!obj) + { + return 0; + } + obj->result = new Ice::AsyncResultPtr(result); + obj->invocation = new InvocationPtr(this); + obj->proxy = _pyProxy; + Py_INCREF(obj->proxy); + obj->communicator = getCommunicatorWrapper(_communicator); + return reinterpret_cast<PyObject*>(obj); +} + +PyObject* +IcePy::AsyncTypedInvocation::end(const Ice::ObjectPrx& proxy, const OperationPtr& op, const Ice::AsyncResultPtr& r) +{ + try + { + if(op.get() != _op.get()) + { + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, "end_" + op->name + + " called with AsyncResult object from begin_" + _op->name); + } + + pair<const Ice::Byte*, const Ice::Byte*> results; + bool ok; + + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking operations. + ok = proxy->___end_ice_invoke(results, r); + } + + if(ok) + { + // + // Unmarshal the results. + // + PyObjectHandle args = unmarshalResults(results); + if(args.get()) + { + // + // If there are no results, return None. If there's only one element + // in the tuple, return the element. Otherwise, return the tuple. + // + assert(PyTuple_Check(args.get())); + if(PyTuple_GET_SIZE(args.get()) == 0) + { + Py_INCREF(Py_None); + return Py_None; + } + else if(PyTuple_GET_SIZE(args.get()) == 1) + { + PyObject* res = PyTuple_GET_ITEM(args.get(), 0); + Py_INCREF(res); + return res; + } + else + { + return args.release(); + } + } + } + else + { + PyObjectHandle ex = unmarshalException(results); + setPythonException(ex.get()); + } + } + catch(const AbortMarshaling&) + { + // Nothing to do. + } + catch(const IceUtil::IllegalArgumentException& ex) + { + PyErr_Format(PyExc_RuntimeError, "%s", STRCAST(ex.reason().c_str())); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + } + + assert(PyErr_Occurred()); + return 0; +} + +string +IcePy::AsyncTypedInvocation::opName() const +{ + return _op->name; +} + +void +IcePy::AsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + try + { + if(ok) + { + if(_response) + { + // + // Unmarshal the results. + // + PyObjectHandle args; + try + { + args = unmarshalResults(results); + if(!args.get()) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } + } + catch(const Ice::Exception& ex) + { + assert(_ex); + callException(_ex, ex); + return; + } + + PyObjectHandle tmp = PyObject_Call(_response, args.get(), 0); + if(PyErr_Occurred()) + { + handleException(); // Callback raised an exception. + } + } + } + else + { + assert(_ex); + PyObjectHandle ex = unmarshalException(results); + callException(_ex, ex.get()); + } + } + catch(const AbortMarshaling&) + { + assert(PyErr_Occurred()); + PyErr_Print(); + } +} + +void +IcePy::AsyncTypedInvocation::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + assert(_ex); + callException(_ex, ex); +} + +void +IcePy::AsyncTypedInvocation::sent(bool sentSynchronously) +{ + if(_sent) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + callSent(_sent, sentSynchronously, true); + } +} + +void +IcePy::AsyncTypedInvocation::checkAsyncTwowayOnly(const Ice::ObjectPrx& proxy) const +{ + 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"); + } + + if((_op->returnType != 0 || !_op->outParams.empty()) && (!_response && (_ex || _sent))) + { + throw IceUtil::IllegalArgumentException(__FILE__, __LINE__, "`" + _op->name + "' requires a response callback"); + } +} + +// +// OldAsyncTypedInvocation +// +IcePy::OldAsyncTypedInvocation::OldAsyncTypedInvocation(const Ice::ObjectPrx& prx, const OperationPtr& op) + : Invocation(prx), TypedInvocation(prx, op), _callback(0) +{ +} + +IcePy::OldAsyncTypedInvocation::~OldAsyncTypedInvocation() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_XDECREF(_callback); +} + +PyObject* +IcePy::OldAsyncTypedInvocation::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. + // + Ice::OutputStreamPtr os; + pair<const Ice::Byte*, const Ice::Byte*> params; + if(!prepareRequest(pyparams, OldAsyncMapping, os, params)) + { + return 0; + } + + bool sentSynchronously = false; + try + { + checkTwowayOnly(_prx); + + Ice::Callback_Object_ice_invokePtr cb = + Ice::newCallback_Object_ice_invoke(this, &OldAsyncTypedInvocation::response, + &OldAsyncTypedInvocation::exception, &OldAsyncTypedInvocation::sent); + + Ice::AsyncResultPtr result; + + // + // 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. + 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); + } + + sentSynchronously = result->sentSynchronously(); + } + catch(const Ice::CommunicatorDestroyedException& ex) + { + // + // CommunicatorDestroyedException can propagate directly. + // + setPythonException(ex); + return 0; + } + catch(const Ice::TwowayOnlyException& ex) + { + // + // Raised by checkTwowayOnly. + // + callException(_callback, _op->name, "ice_exception", ex); + } + catch(const Ice::Exception&) + { + // + // No other exceptions should be raised by begin_ice_invoke. + // + assert(false); + } + + PyRETURN_BOOL(sentSynchronously); +} + +void +IcePy::OldAsyncTypedInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + assert(_callback); + + try + { + if(ok) + { + // + // Unmarshal the results. + // + PyObjectHandle args; + try + { + args = unmarshalResults(results); + if(!args.get()) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } + } + catch(const Ice::Exception& ex) + { + callException(_callback, _op->name, "ice_exception", ex); + return; + } + + const string methodName = "ice_response"; + if(!PyObject_HasAttrString(_callback, STRCAST(methodName.c_str()))) + { + 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())); + } + 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. + } + } + } + else + { + PyObjectHandle ex = unmarshalException(results); + callException(_callback, _op->name, "ice_exception", ex.get()); + } + } + catch(const AbortMarshaling&) + { + assert(PyErr_Occurred()); + PyErr_Print(); + } +} + +void +IcePy::OldAsyncTypedInvocation::exception(const Ice::Exception& ex) +{ + 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); +} + +// +// SyncBlobjectInvocation +// +IcePy::SyncBlobjectInvocation::SyncBlobjectInvocation(const Ice::ObjectPrx& prx) + : Invocation(prx) +{ +} + +PyObject* +IcePy::SyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) +{ + char* operation; + PyObject* mode; + PyObject* inParams; + PyObject* operationModeType = lookupType("Ice.OperationMode"); + PyObject* ctx = 0; +#if PY_VERSION_HEX >= 0x03000000 + if(!PyArg_ParseTuple(args, STRCAST("sO!O!|O"), &operation, operationModeType, &mode, &PyBytes_Type, &inParams, + &ctx)) + { + return 0; + } +#else + if(!PyArg_ParseTuple(args, STRCAST("sO!O!|O"), &operation, operationModeType, &mode, &PyBuffer_Type, &inParams, + &ctx)) + { + return 0; + } +#endif + + PyObjectHandle modeValue = PyObject_GetAttrString(mode, STRCAST("value")); + Ice::OperationMode sendMode = (Ice::OperationMode)static_cast<int>(PyLong_AsLong(modeValue.get())); + assert(!PyErr_Occurred()); + +#if PY_VERSION_HEX >= 0x03000000 + Py_ssize_t sz = PyBytes_GET_SIZE(inParams); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = reinterpret_cast<Ice::Byte*>(PyBytes_AS_STRING(inParams)); + in.second = in.first + sz; + } +#else + // + // Use the array API to avoid copying the data. + // + char* charBuf = 0; + Py_ssize_t sz = inParams->ob_type->tp_as_buffer->bf_getcharbuffer(inParams, 0, &charBuf); + const Ice::Byte* mem = reinterpret_cast<const Ice::Byte*>(charBuf); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = mem; + in.second = mem + sz; + } +#endif + + try + { + vector<Ice::Byte> out; + + bool ok; + if(ctx == 0 || ctx == Py_None) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + ok = _prx->ice_invoke(operation, sendMode, in, out); + } + else + { + Ice::Context context; + if(!dictionaryToContext(ctx, context)) + { + return 0; + } + + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + ok = _prx->ice_invoke(operation, sendMode, in, out, context); + } + + // + // Prepare the result as a tuple of the bool and out param buffer. + // + PyObjectHandle result = PyTuple_New(2); + if(!result.get()) + { + throwPythonException(); + } + + if(PyTuple_SET_ITEM(result.get(), 0, ok ? getTrue() : getFalse()) < 0) + { + throwPythonException(); + } + +#if PY_VERSION_HEX >= 0x03000000 + PyObjectHandle op; + if(out.empty()) + { + op = PyBytes_FromString(""); + } + else + { + op = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(&out[0]), out.size()); + } + if(!op.get()) + { + throwPythonException(); + } +#else + // + // Create the output buffer and copy in the outParams. + // + PyObjectHandle op = PyBuffer_New(out.size()); + if(!op.get()) + { + throwPythonException(); + } + if(!out.empty()) + { + void* buf; + Py_ssize_t sz; + if(PyObject_AsWriteBuffer(op.get(), &buf, &sz)) + { + throwPythonException(); + } + memcpy(buf, &out[0], sz); + } +#endif + + if(PyTuple_SET_ITEM(result.get(), 1, op.get()) < 0) + { + throwPythonException(); + } + op.release(); // PyTuple_SET_ITEM steals a reference. + + return result.release(); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } +} + +// +// AsyncBlobjectInvocation +// +IcePy::AsyncBlobjectInvocation::AsyncBlobjectInvocation(const Ice::ObjectPrx& prx, PyObject* pyProxy) : + Invocation(prx), _pyProxy(pyProxy), _response(0), _ex(0), _sent(0) +{ + Py_INCREF(_pyProxy); +} + +IcePy::AsyncBlobjectInvocation::~AsyncBlobjectInvocation() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_pyProxy); + Py_XDECREF(_response); + Py_XDECREF(_ex); + Py_XDECREF(_sent); +} + +PyObject* +IcePy::AsyncBlobjectInvocation::invoke(PyObject* args, PyObject* kwds) +{ + static char* argNames[] = + { + const_cast<char*>("op"), + const_cast<char*>("mode"), + const_cast<char*>("inParams"), + const_cast<char*>("_response"), + const_cast<char*>("_ex"), + const_cast<char*>("_sent"), + const_cast<char*>("_ctx"), + 0 + }; + char* operation; + PyObject* mode; + PyObject* inParams; + PyObject* operationModeType = lookupType("Ice.OperationMode"); + PyObject* response = Py_None; + PyObject* ex = Py_None; + PyObject* sent = Py_None; + PyObject* pyctx = Py_None; +#if PY_VERSION_HEX >= 0x03000000 + if(!PyArg_ParseTupleAndKeywords(args, kwds, STRCAST("sO!O!|OOOO"), argNames, &operation, operationModeType, &mode, + &PyBytes_Type, &inParams, &response, &ex, &sent, &pyctx)) + { + return 0; + } +#else + if(!PyArg_ParseTupleAndKeywords(args, kwds, STRCAST("sO!O!|OOOO"), argNames, &operation, operationModeType, &mode, + &PyBuffer_Type, &inParams, &response, &ex, &sent, &pyctx)) + { + return 0; + } +#endif + + _op = operation; + + PyObjectHandle modeValue = PyObject_GetAttrString(mode, STRCAST("value")); + Ice::OperationMode sendMode = (Ice::OperationMode)static_cast<int>(PyLong_AsLong(modeValue.get())); + assert(!PyErr_Occurred()); + + if(PyCallable_Check(response)) + { + _response = response; + Py_INCREF(_response); + } + else if(response != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("response callback must be a callable object or None")); + return 0; + } + + if(PyCallable_Check(ex)) + { + _ex = ex; + Py_INCREF(_ex); + } + else if(ex != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("exception callback must be a callable object or None")); + return 0; + } + + if(PyCallable_Check(sent)) + { + _sent = sent; + Py_INCREF(_sent); + } + else if(sent != Py_None) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("sent callback must be a callable object or None")); + return 0; + } + + if(!_ex && (_response || _sent)) + { + PyErr_Format(PyExc_RuntimeError, + STRCAST("exception callback must also be provided when response or sent callbacks are used")); + return 0; + } + + if(pyctx != Py_None && !PyDict_Check(pyctx)) + { + PyErr_Format(PyExc_RuntimeError, STRCAST("context must be a dictionary or None")); + return 0; + } + +#if PY_VERSION_HEX >= 0x03000000 + Py_ssize_t sz = PyBytes_GET_SIZE(inParams); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = reinterpret_cast<Ice::Byte*>(PyBytes_AS_STRING(inParams)); + in.second = in.first + sz; + } +#else + // + // Use the array API to avoid copying the data. + // + char* charBuf = 0; + Py_ssize_t sz = inParams->ob_type->tp_as_buffer->bf_getcharbuffer(inParams, 0, &charBuf); + const Ice::Byte* mem = reinterpret_cast<const Ice::Byte*>(charBuf); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = mem; + in.second = mem + sz; + } +#endif + + Ice::AsyncResultPtr result; + try + { + Ice::Callback_Object_ice_invokePtr cb; + if(_response || _ex || _sent) + { + cb = Ice::newCallback_Object_ice_invoke(this, &AsyncBlobjectInvocation::response, + &AsyncBlobjectInvocation::exception, + &AsyncBlobjectInvocation::sent); + } + + if(pyctx == Py_None) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + if(cb) + { + result = _prx->begin_ice_invoke(operation, sendMode, in, cb); + } + else + { + result = _prx->begin_ice_invoke(operation, sendMode, in); + } + } + else + { + Ice::Context context; + if(!dictionaryToContext(pyctx, context)) + { + return 0; + } + + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + if(cb) + { + result = _prx->begin_ice_invoke(operation, sendMode, in, context, cb); + } + else + { + result = _prx->begin_ice_invoke(operation, sendMode, in, context); + } + } + } + catch(const Ice::CommunicatorDestroyedException& ex) + { + // + // CommunicatorDestroyedException is the only exception that can propagate directly. + // + setPythonException(ex); + return 0; + } + catch(const Ice::Exception&) + { + // + // No other exceptions should be raised by begin_ice_invoke. + // + assert(false); + } + + assert(result); + AsyncResultObject* obj = asyncResultNew(&AsyncResultType, 0, 0); + if(!obj) + { + return 0; + } + obj->result = new Ice::AsyncResultPtr(result); + obj->invocation = new InvocationPtr(this); + obj->proxy = _pyProxy; + Py_INCREF(obj->proxy); + obj->communicator = getCommunicatorWrapper(_prx->ice_getCommunicator()); + return reinterpret_cast<PyObject*>(obj); +} + +PyObject* +IcePy::AsyncBlobjectInvocation::end(const Ice::ObjectPrx& proxy, const Ice::AsyncResultPtr& r) +{ + try + { + pair<const Ice::Byte*, const Ice::Byte*> results; + bool ok; + + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking operations. + ok = proxy->___end_ice_invoke(results, r); + } + + // + // Prepare the results as a tuple of the bool and out param buffer. + // + PyObjectHandle args = PyTuple_New(2); + if(!args.get()) + { + return 0; + } + + if(PyTuple_SET_ITEM(args.get(), 0, ok ? getTrue() : getFalse()) < 0) + { + return 0; + } + +#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()) + { + return 0; + } +#else + // + // Create the output buffer and copy in the outParams. + // + PyObjectHandle op = PyBuffer_New(results.second - results.first); + if(!op.get()) + { + return 0; + } + + void* buf; + Py_ssize_t sz; + if(PyObject_AsWriteBuffer(op.get(), &buf, &sz)) + { + return 0; + } + assert(sz == results.second - results.first); + 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. + + return args.release(); + } + catch(const AbortMarshaling&) + { + // Nothing to do. + } + catch(const IceUtil::IllegalArgumentException& ex) + { + PyErr_Format(PyExc_RuntimeError, "%s", STRCAST(ex.reason().c_str())); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + } + + assert(PyErr_Occurred()); + return 0; +} + +void +IcePy::AsyncBlobjectInvocation::response(bool ok, const pair<const Ice::Byte*, const Ice::Byte*>& results) +{ + if(_response) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + // + // 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; + } + + if(PyTuple_SET_ITEM(args.get(), 0, ok ? getTrue() : getFalse()) < 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; + } +#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; + } + + 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. + + PyObjectHandle tmp = PyObject_Call(_response, args.get(), 0); + if(PyErr_Occurred()) + { + handleException(); // Callback raised an exception. + } + } +} + +void +IcePy::AsyncBlobjectInvocation::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + assert(_ex); + callException(_ex, ex); +} + +void +IcePy::AsyncBlobjectInvocation::sent(bool sentSynchronously) +{ + if(_sent) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + callSent(_sent, sentSynchronously, true); + } +} + +// +// OldAsyncBlobjectInvocation +// +IcePy::OldAsyncBlobjectInvocation::OldAsyncBlobjectInvocation(const Ice::ObjectPrx& prx) : + Invocation(prx), _callback(0) +{ +} + +IcePy::OldAsyncBlobjectInvocation::~OldAsyncBlobjectInvocation() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_XDECREF(_callback); +} + +PyObject* +IcePy::OldAsyncBlobjectInvocation::invoke(PyObject* args, PyObject* /* kwds */) +{ + char* operation; + PyObject* mode; + PyObject* inParams; + 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, + &PyBytes_Type, &inParams, &ctx)) + { + return 0; + } +#else + if(!PyArg_ParseTuple(args, STRCAST("OsO!O!|O"), &_callback, &operation, operationModeType, &mode, + &PyBuffer_Type, &inParams, &ctx)) + { + return 0; + } +#endif + + Py_INCREF(_callback); + _op = operation; + + PyObjectHandle modeValue = PyObject_GetAttrString(mode, STRCAST("value")); + Ice::OperationMode sendMode = (Ice::OperationMode)static_cast<int>(PyLong_AsLong(modeValue.get())); + assert(!PyErr_Occurred()); + +#if PY_VERSION_HEX >= 0x03000000 + Py_ssize_t sz = PyBytes_GET_SIZE(inParams); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = reinterpret_cast<Ice::Byte*>(PyBytes_AS_STRING(inParams)); + in.second = in.first + sz; + } +#else + // + // Use the array API to avoid copying the data. + // + char* charBuf = 0; + Py_ssize_t sz = inParams->ob_type->tp_as_buffer->bf_getcharbuffer(inParams, 0, &charBuf); + const Ice::Byte* mem = reinterpret_cast<const Ice::Byte*>(charBuf); + pair<const ::Ice::Byte*, const ::Ice::Byte*> in(static_cast<const Ice::Byte*>(0), + static_cast<const Ice::Byte*>(0)); + if(sz > 0) + { + in.first = mem; + in.second = mem + sz; + } +#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); + + 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) + { + // + // CommunicatorDestroyedException is the only exception that can propagate directly. + // + setPythonException(ex); + return 0; + } + catch(const Ice::Exception&) + { + // + // No other exceptions should be raised by begin_ice_invoke. + // + assert(false); + } + + PyRETURN_BOOL(sentSynchronously); +} + +void +IcePy::OldAsyncBlobjectInvocation::response(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()) + { + assert(PyErr_Occurred()); + PyErr_Print(); + return; + } + + if(PyTuple_SET_ITEM(args.get(), 0, ok ? getTrue() : getFalse()) < 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; + } +#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; + } + + 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. + + 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. + } + } + } + catch(const Ice::Exception& ex) + { + ostringstream ostr; + ostr << "Exception raised by AMI callback for operation `ice_invoke_async':" << ex; + string str = ostr.str(); + PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + } +} + +void +IcePy::OldAsyncBlobjectInvocation::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + callException(_callback, "ice_invoke", "ice_exception", ex); +} + +void +IcePy::OldAsyncBlobjectInvocation::sent(bool) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + callSent(_callback, "ice_sent", false, false); +} + +// +// TypedUpcall +// +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) +{ +} + +void +IcePy::TypedUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, const Ice::Byte*>& inBytes, + const Ice::Current& current) +{ + // + // 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()) + { + throwPythonException(); + } + + if(!_op->inParams.empty()) + { + Ice::InputStreamPtr is = Ice::wrapInputStream(_communicator, inBytes); + + // + // Store a pointer to a local SlicedDataUtil object as the stream's closure. + // This is necessary to support object unmarshaling (see ObjectReader). + // + SlicedDataUtil util; + assert(!is->closure()); + is->closure(&util); + + try + { + is->startEncapsulation(); + + ParamInfoList::iterator p; + + // + // Unmarshal the required parameters. + // + for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p) + { + ParamInfoPtr info = *p; + if(!info->optional) + { + void* closure = reinterpret_cast<void*>(info->pos + offset); + info->type->unmarshal(is, info, args.get(), closure, false, &info->metaData); + } + } + + // + // Unmarshal the optional parameters. + // + for(p = _op->optionalInParams.begin(); p != _op->optionalInParams.end(); ++p) + { + ParamInfoPtr info = *p; + if(is->readOptional(info->tag, info->type->optionalFormat())) + { + void* closure = reinterpret_cast<void*>(info->pos + offset); + 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. + } + } + + if(_op->sendsClasses) + { + is->readPendingObjects(); + } + + is->endEncapsulation(); + + util.update(); + } + catch(const AbortMarshaling&) + { + throwPythonException(); + } + } + + // + // 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; + } + + if(!_op->amd) + { + response(result.get(), current.encoding); + } +} + +void +IcePy::TypedUpcall::response(PyObject* args, const Ice::EncodingVersion& encoding) +{ + if(_finished) + { + // + // This method could be called more than once if the application calls + // ice_response multiple times. We ignore subsequent calls. + // + return; + } + + _finished = true; + + 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, ...). + // + Ice::OutputStreamPtr os = Ice::createOutputStream(_communicator); + try + { + Py_ssize_t numResults = static_cast<Py_ssize_t>(_op->outParams.size()); + if(_op->returnType) + { + numResults++; + } + + if(numResults > 1 && (!PyTuple_Check(args) || PyTuple_GET_SIZE(args) != numResults)) + { + ostringstream ostr; + ostr << "operation `" << fixIdent(_op->name) << "' should return a tuple of length " << numResults; + string str = ostr.str(); + PyErr_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + 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 + { + t = PyTuple_New(1); + if(!t.get()) + { + throw AbortMarshaling(); + } + PyTuple_SET_ITEM(t.get(), 0, args); + } + Py_INCREF(args); + + os->startEncapsulation(encoding, _op->format); + + ObjectMap objectMap; + ParamInfoList::iterator p; + + // + // 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 << "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); + } + } + + // + // 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) + { + 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); + } + } + + if(_op->returnsClasses) + { + os->writePendingObjects(); + } + + os->endEncapsulation(); + + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_response(true, os->finished()); + } + catch(const AbortMarshaling&) + { + throwPythonException(); + } + } + catch(const Ice::Exception& ex) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_exception(ex); + } +} + +void +IcePy::TypedUpcall::exception(PyException& ex, const Ice::EncodingVersion& encoding) +{ + 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 + { + // + // A servant that calls sys.exit() will raise the SystemExit exception. + // This is normally caught by the interpreter, causing it to exit. + // However, we have no way to pass this exception to the interpreter, + // so we act on it directly. + // + ex.checkSystemExit(); + + PyObject* userExceptionType = lookupType("Ice.UserException"); + + if(PyObject_IsInstance(ex.ex.get(), userExceptionType)) + { + // + // Get the exception's type and verify that it is legal to be thrown from this operation. + // + PyObjectHandle iceType = PyObject_GetAttrString(ex.ex.get(), STRCAST("_ice_type")); + assert(iceType.get()); + ExceptionInfoPtr info = ExceptionInfoPtr::dynamicCast(getException(iceType.get())); + assert(info); + if(!validateException(ex.ex.get())) + { + ex.raise(); // Raises UnknownUserException. + } + else + { + Ice::OutputStreamPtr os = Ice::createOutputStream(_communicator); + os->startEncapsulation(encoding, _op->format); + + ExceptionWriter writer(_communicator, ex.ex, info); + os->writeException(writer); + + os->endEncapsulation(); + + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_response(false, os->finished()); + } + } + else + { + ex.raise(); + } + } + catch(const AbortMarshaling&) + { + throwPythonException(); + } + } + catch(const Ice::Exception& ex) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + _callback->ice_exception(ex); + } +} + +bool +IcePy::TypedUpcall::validateException(PyObject* ex) const +{ + for(ExceptionInfoList::const_iterator p = _op->exceptions.begin(); p != _op->exceptions.end(); ++p) + { + if(PyObject_IsInstance(ex, (*p)->pythonType.get())) + { + return true; + } + } + + return false; +} + +// +// BlobjectUpcall +// +IcePy::BlobjectUpcall::BlobjectUpcall(bool amd, const Ice::AMD_Object_ice_invokePtr& callback) : + _amd(amd), _callback(callback), _finished(false) +{ +} + +void +IcePy::BlobjectUpcall::dispatch(PyObject* servant, const pair<const Ice::Byte*, const Ice::Byte*>& inBytes, + const Ice::Current& current) +{ + Ice::CommunicatorPtr communicator = current.adapter->getCommunicator(); + + 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()) + { + throwPythonException(); + } + + PyObjectHandle ip; + +#if PY_VERSION_HEX >= 0x03000000 + if(inBytes.second == inBytes.first) + { + ip = PyBytes_FromString(""); + } + else + { + ip = PyBytes_FromStringAndSize(reinterpret_cast<const char*>(inBytes.first), inBytes.second - inBytes.first); + } +#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. + // + if(!_amd) + { + ip = PyBuffer_FromMemory((void*)inBytes.first, inBytes.second - inBytes.first); + if(!ip.get()) + { + throwPythonException(); + } + } + else + { + 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); + } +#endif + + if(PyTuple_SET_ITEM(args.get(), start, ip.get()) < 0) + { + throwPythonException(); + } + ++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; + } + + if(!_amd) + { + response(result.get(), current.encoding); + } +} + +void +IcePy::BlobjectUpcall::response(PyObject* args, const Ice::EncodingVersion&) +{ + if(_finished) + { + // + // This method could be called more than once if the application calls + // ice_response multiple times. We ignore subsequent calls. + // + return; + } + + _finished = true; + + // + // 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) + { + name += "_async"; + } + 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; + + arg = PyTuple_GET_ITEM(args, 1); + +#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__); + } + + 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_Warn(PyExc_RuntimeWarning, const_cast<char*>(str.c_str())); + throw Ice::MarshalException(__FILE__, __LINE__); + } + + 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&) +{ + 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 + { + // + // A servant that calls sys.exit() will raise the SystemExit exception. + // This is normally caught by the interpreter, causing it to exit. + // However, we have no way to pass this exception to the interpreter, + // so we act on it directly. + // + ex.checkSystemExit(); + + ex.raise(); + } + catch(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) +{ + 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 SyncTypedInvocation(p, op); + return i->invoke(args); +} + +PyObject* +IcePy::beginBuiltin(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 AsyncTypedInvocation(p, proxy, op); + return i->invoke(args); +} + +PyObject* +IcePy::endBuiltin(PyObject* proxy, const string& builtin, PyObject* args) +{ + PyObject* result; + if(!PyArg_ParseTuple(args, STRCAST("O!"), &AsyncResultType, &result)) + { + return 0; + } + + 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); + + AsyncResultObject* ar = reinterpret_cast<AsyncResultObject*>(result); + assert(ar); + AsyncTypedInvocationPtr i = AsyncTypedInvocationPtr::dynamicCast(*ar->invocation); + if(!i) + { + PyErr_Format(PyExc_ValueError, STRCAST("invalid AsyncResult object passed to end_%s"), op->name.c_str()); + return 0; + } + Ice::ObjectPrx p = getProxy(proxy); + return i->end(p, op, *ar->result); +} + +PyObject* +IcePy::iceInvoke(PyObject* proxy, PyObject* args) +{ + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new SyncBlobjectInvocation(p); + return i->invoke(args); +} + +PyObject* +IcePy::iceInvokeAsync(PyObject* proxy, PyObject* args) +{ + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new OldAsyncBlobjectInvocation(p); + return i->invoke(args); +} + +PyObject* +IcePy::beginIceInvoke(PyObject* proxy, PyObject* args, PyObject* kwds) +{ + Ice::ObjectPrx p = getProxy(proxy); + InvocationPtr i = new AsyncBlobjectInvocation(p, proxy); + return i->invoke(args, kwds); +} + +PyObject* +IcePy::endIceInvoke(PyObject* proxy, PyObject* args) +{ + PyObject* result; + if(!PyArg_ParseTuple(args, STRCAST("O!"), &AsyncResultType, &result)) + { + return 0; + } + + AsyncResultObject* ar = reinterpret_cast<AsyncResultObject*>(result); + assert(ar); + AsyncBlobjectInvocationPtr i = AsyncBlobjectInvocationPtr::dynamicCast(*ar->invocation); + if(!i) + { + PyErr_Format(PyExc_ValueError, STRCAST("invalid AsyncResult object passed to end_ice_invoke")); + return 0; + } + Ice::ObjectPrx p = getProxy(proxy); + return i->end(p, *ar->result); +} + +PyObject* +IcePy::createAsyncResult(const Ice::AsyncResultPtr& r, PyObject* proxy, PyObject* connection, PyObject* communicator) +{ + AsyncResultObject* obj = asyncResultNew(&AsyncResultType, 0, 0); + if(!obj) + { + 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); + return reinterpret_cast<PyObject*>(obj); +} + +Ice::AsyncResultPtr +IcePy::getAsyncResult(PyObject* p) +{ + assert(PyObject_IsInstance(p, reinterpret_cast<PyObject*>(&AsyncResultType)) == 1); + AsyncResultObject* obj = reinterpret_cast<AsyncResultObject*>(p); + return *obj->result; +} + +IcePy::FlushCallback::FlushCallback(PyObject* ex, PyObject* sent, const string& op) : + _ex(ex), _sent(sent), _op(op) +{ + assert(_ex); + Py_INCREF(_ex); + Py_XINCREF(_sent); +} + +IcePy::FlushCallback::~FlushCallback() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_ex); + Py_XDECREF(_sent); +} + +void +IcePy::FlushCallback::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + callException(_ex, ex); +} + +void +IcePy::FlushCallback::sent(bool sentSynchronously) +{ + if(_sent) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + callSent(_sent, sentSynchronously, true); + } +} + +IcePy::GetConnectionCallback::GetConnectionCallback(const Ice::CommunicatorPtr& communicator, + PyObject* response, PyObject* ex, const string& op) : + _communicator(communicator), _response(response), _ex(ex), _op(op) +{ + assert(_response); + Py_INCREF(_response); + Py_XINCREF(_ex); +} + +IcePy::GetConnectionCallback::~GetConnectionCallback() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_response); + Py_XDECREF(_ex); +} + +void +IcePy::GetConnectionCallback::response(const Ice::ConnectionPtr& conn) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + PyObjectHandle pyConn = createConnection(conn, _communicator); + PyObjectHandle args = Py_BuildValue(STRCAST("(O)"), pyConn.get()); + PyObjectHandle tmp = PyObject_Call(_response, args.get(), 0); + if(PyErr_Occurred()) + { + handleException(); // Callback raised an exception. + } +} + +void +IcePy::GetConnectionCallback::exception(const Ice::Exception& ex) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + callException(_ex, ex); +} + +// +// ServantWrapper implementation. +// +IcePy::ServantWrapper::ServantWrapper(PyObject* servant) : + _servant(servant) +{ + Py_INCREF(_servant); +} + +IcePy::ServantWrapper::~ServantWrapper() +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_servant); +} + +PyObject* +IcePy::ServantWrapper::getObject() +{ + Py_INCREF(_servant); + return _servant; +} + +// +// TypedServantWrapper implementation. +// +IcePy::TypedServantWrapper::TypedServantWrapper(PyObject* servant) : + ServantWrapper(servant), _lastOp(_operationMap.end()) +{ +} + +void +IcePy::TypedServantWrapper::ice_invoke_async(const Ice::AMD_Object_ice_invokePtr& cb, + const pair<const Ice::Byte*, const Ice::Byte*>& inParams, + const Ice::Current& current) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + try + { + // + // Locate the Operation object. As an optimization we keep a reference + // to the most recent operation we've dispatched, so check that first. + // + OperationPtr op; + if(_lastOp != _operationMap.end() && _lastOp->first == current.operation) + { + op = _lastOp->second; + } + else + { + // + // Next check our cache of operations. + // + _lastOp = _operationMap.find(current.operation); + if(_lastOp == _operationMap.end()) + { + // + // Look for the Operation object in the servant's type. + // + string attrName = "_op_" + current.operation; + PyObjectHandle h = PyObject_GetAttrString((PyObject*)_servant->ob_type, + const_cast<char*>(attrName.c_str())); + if(!h.get()) + { + PyErr_Clear(); + Ice::OperationNotExistException ex(__FILE__, __LINE__); + ex.id = current.id; + ex.facet = current.facet; + ex.operation = current.operation; + throw ex; + } + + assert(PyObject_IsInstance(h.get(), reinterpret_cast<PyObject*>(&OperationType)) == 1); + OperationObject* obj = reinterpret_cast<OperationObject*>(h.get()); + op = *obj->op; + _lastOp = _operationMap.insert(OperationMap::value_type(current.operation, op)).first; + } + else + { + op = _lastOp->second; + } + } + + // + // See bug 4976. + // + if(!op->pseudoOp) + { + __checkMode(op->mode, current.mode); + } + + UpcallPtr up = new TypedUpcall(op, cb, current.adapter->getCommunicator()); + up->dispatch(_servant, inParams, current); + } + catch(const Ice::Exception& ex) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + cb->ice_exception(ex); + } +} + +// +// BlobjectServantWrapper implementation. +// +IcePy::BlobjectServantWrapper::BlobjectServantWrapper(PyObject* servant, bool amd) : + ServantWrapper(servant), _amd(amd) +{ +} + +void +IcePy::BlobjectServantWrapper::ice_invoke_async(const Ice::AMD_Object_ice_invokePtr& cb, + const pair<const Ice::Byte*, const Ice::Byte*>& inParams, + const Ice::Current& current) +{ + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + try + { + UpcallPtr up = new BlobjectUpcall(_amd, cb); + up->dispatch(_servant, inParams, current); + } + catch(const Ice::Exception& ex) + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking calls. + cb->ice_exception(ex); + } +} + +IcePy::ServantWrapperPtr +IcePy::createServantWrapper(PyObject* servant) +{ + ServantWrapperPtr wrapper; + PyObject* blobjectType = lookupType("Ice.Blobject"); + PyObject* blobjectAsyncType = lookupType("Ice.BlobjectAsync"); + if(PyObject_IsInstance(servant, blobjectType)) + { + return new BlobjectServantWrapper(servant, false); + } + else if(PyObject_IsInstance(servant, blobjectAsyncType)) + { + return new BlobjectServantWrapper(servant, true); + } + + return new TypedServantWrapper(servant); +} |