From a9b9b40570e6cf26ac23cd49b043d8bc2e878240 Mon Sep 17 00:00:00 2001 From: Lionel Elie Mamane Date: Sat, 20 Aug 2011 11:39:13 +0200 Subject: pyuno: allow uno structs to be initliased with keyword arguments --- pyuno/source/module/pyuno_module.cxx | 182 +++++++++++++++++++++++++++-------- pyuno/source/module/uno.py | 37 ++++--- 2 files changed, 164 insertions(+), 55 deletions(-) diff --git a/pyuno/source/module/pyuno_module.cxx b/pyuno/source/module/pyuno_module.cxx index 1bada6d10f32..973ef00c8b2e 100644 --- a/pyuno/source/module/pyuno_module.cxx +++ b/pyuno/source/module/pyuno_module.cxx @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; eval:(c-set-style "bsd"); tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -28,12 +28,16 @@ #include "pyuno_impl.hxx" +#include +#include + #include #include #include #include +#include #include #include #include @@ -50,6 +54,7 @@ using osl::Module; using rtl::OString; using rtl::OUString; +using rtl::OUStringHash; using rtl::OUStringToOString; using rtl::OUStringBuffer; using rtl::OStringBuffer; @@ -83,35 +88,133 @@ namespace { // dictionary of all extra keyword arguments; the keys are strings, // which are the names that were used to identify the arguments. If // they exist, these arguments must be the last one in the list. -sal_Int32 fillStructWithInitializer( + +class fillStructState +{ + typedef std::unordered_map initialised_t; + // Keyword arguments used + PyObject *used; + // Which structure members are initialised + std::unordered_map initialised; + // How many positional arguments are consumed + // This is always the so-many first ones + sal_Int32 nPosConsumed; + // The total number of members set, either by a keyword argument or a positional argument + unsigned int nMembersSet; + +public: + fillStructState() + : used (PyDict_New()) + , initialised () + , nPosConsumed (0) + , nMembersSet (0) + { + if ( ! used ) + throw RuntimeException(OUString(RTL_CONSTASCII_USTRINGPARAM("pyuno._createUnoStructHelper failed to create new dictionary")), Reference< XInterface > ()); + } + ~fillStructState() + { + Py_DECREF(used); + } + void setUsed(PyObject *key) + { + PyDict_SetItem(used, key, Py_True); + } + bool isUsed(PyObject *key) const + { + return Py_True == PyDict_GetItem(used, key); + } + void setInitialised(OUString key, int pos = -1) + { + if (initialised[key]) + { + OUStringBuffer buf; + buf.appendAscii( "pyuno._createUnoStructHelper: member '"); + buf.append(key); + buf.appendAscii( "'"); + if ( pos >= 0 ) + { + buf.appendAscii( " at position "); + buf.append(pos); + } + buf.appendAscii( " initialised multiple times."); + throw RuntimeException(buf.makeStringAndClear(), Reference< XInterface > ()); + } + initialised[key] = true; + ++nMembersSet; + if ( pos >= 0 ) + ++nPosConsumed; + } + bool isInitialised(OUString key) + { + return initialised[key]; + } + PyObject *getUsed() const + { + return used; + } + sal_Int32 getCntConsumed() const + { + return nPosConsumed; + } + int getCntMembersSet() const + { + return nMembersSet; + } +}; + +static void fillStruct( const Reference< XInvocation2 > &inv, typelib_CompoundTypeDescription *pCompType, PyObject *initializer, + PyObject *kwinitializer, + fillStructState &state, const Runtime &runtime) throw ( RuntimeException ) { - sal_Int32 nIndex = 0; if( pCompType->pBaseTypeDescription ) - nIndex = fillStructWithInitializer( - inv, pCompType->pBaseTypeDescription, initializer, runtime ); + fillStruct( inv, pCompType->pBaseTypeDescription, initializer, kwinitializer, state, runtime ); - sal_Int32 nTupleSize = PyTuple_Size(initializer); - int i; - for( i = 0 ; i < pCompType->nMembers ; i ++ ) + const sal_Int32 nMembers = pCompType->nMembers; + { + for( int i = 0 ; i < nMembers ; i ++ ) + { + const OUString OUMemberName (pCompType->ppMemberNames[i]); + PyObject *pyMemberName = PyString_FromString(::rtl::OUStringToOString( OUMemberName, RTL_TEXTENCODING_UTF8 ).getStr()); + if ( PyObject *element = PyDict_GetItem(kwinitializer, pyMemberName ) ) + { + state.setInitialised(OUMemberName); + state.setUsed(pyMemberName); + Any a = runtime.pyObject2Any( element, ACCEPT_UNO_ANY ); + inv->setValue( OUMemberName, a ); + } + } + } + { + const int remainingPosInitialisers = PyTuple_Size(initializer) - state.getCntConsumed(); + for( int i = 0 ; i < remainingPosInitialisers && i < nMembers ; i ++ ) + { + const int tupleIndex = state.getCntConsumed(); + const OUString pMemberName (pCompType->ppMemberNames[i]); + state.setInitialised(pMemberName, tupleIndex); + PyObject *element = PyTuple_GetItem( initializer, tupleIndex ); + Any a = runtime.pyObject2Any( element, ACCEPT_UNO_ANY ); + inv->setValue( pMemberName, a ); + } + } + for ( int i = 0; i < nMembers ; ++i) { - // LEM TODO: and if too many? Silently ignored? - if( i + nIndex >= nTupleSize ) + const OUString memberName (pCompType->ppMemberNames[i]); + if ( ! state.isInitialised( memberName ) ) { OUStringBuffer buf; - buf.appendAscii( "pyuno._createUnoStructHelper: too few elements in the initializer tuple,"); - buf.appendAscii( "expected at least " ).append( nIndex + pCompType->nMembers ); - buf.appendAscii( ", got " ).append( nTupleSize ); + buf.appendAscii( "pyuno._createUnoStructHelper: member '"); + buf.append(memberName); + buf.appendAscii( "' of struct type '"); + buf.append(pCompType->aBase.pTypeName); + buf.appendAscii( "' not given a value."); throw RuntimeException(buf.makeStringAndClear(), Reference< XInterface > ()); } - PyObject *element = PyTuple_GetItem( initializer, i + nIndex ); - Any a = runtime.pyObject2Any( element, ACCEPT_UNO_ANY ); - inv->setValue( pCompType->ppMemberNames[i], a ); } - return i+nIndex; } OUString getLibDir() @@ -249,7 +352,7 @@ PyObject * extractOneStringArg( PyObject *args, char const *funcName ) return obj; } -static PyObject *createUnoStructHelper(PyObject *, PyObject* args ) +static PyObject *createUnoStructHelper(PyObject *, PyObject* args, PyObject* keywordArgs) { Any IdlStruct; PyRef ret; @@ -265,7 +368,7 @@ static PyObject *createUnoStructHelper(PyObject *, PyObject* args ) // in Python 2, only PyString_Check returns true. if(PyString_Check(structName) || PyUnicode_Check(structName)) { - if( PyTuple_Check( initializer ) ) + if( PyTuple_Check( initializer ) && PyDict_Check ( keywordArgs ) ) { OUString typeName( OUString::createFromAscii(PyString_AsString(structName))); RuntimeCargo *c = runtime.getImpl()->cargo; @@ -275,28 +378,25 @@ static PyObject *createUnoStructHelper(PyObject *, PyObject* args ) idl_class->createObject (IdlStruct); PyUNO *me = (PyUNO*)PyUNO_new_UNCHECKED( IdlStruct, c->xInvocation ); PyRef returnCandidate( (PyObject*)me, SAL_NO_ACQUIRE ); - if( PyTuple_Size( initializer ) > 0 ) + TypeDescription desc( typeName ); + OSL_ASSERT( desc.is() ); // could already instantiate an XInvocation2 ! + + typelib_CompoundTypeDescription *pCompType = + ( typelib_CompoundTypeDescription * ) desc.get(); + fillStructState state; + if ( PyTuple_Size( initializer ) > 0 || PyDict_Size( keywordArgs ) > 0 ) + fillStruct( me->members->xInvocation, pCompType, initializer, keywordArgs, state, runtime ); + if( state.getCntConsumed() != PyTuple_Size(initializer) ) { - TypeDescription desc( typeName ); - OSL_ASSERT( desc.is() ); // could already instantiate an XInvocation2 ! - - typelib_CompoundTypeDescription *pCompType = - ( typelib_CompoundTypeDescription * ) desc.get(); - sal_Int32 n = fillStructWithInitializer( - me->members->xInvocation, pCompType, initializer, runtime ); - if( n != PyTuple_Size(initializer) ) - { - OUStringBuffer buf; - buf.appendAscii( "pyuno._createUnoStructHelper: wrong number of "); - buf.appendAscii( "elements in the initializer list, expected " ); - buf.append( n ); - buf.appendAscii( ", got " ); - buf.append( (sal_Int32) PyTuple_Size(initializer) ); - throw RuntimeException( - buf.makeStringAndClear(), Reference< XInterface > ()); - } + OUStringBuffer buf; + buf.appendAscii( "pyuno._createUnoStructHelper: too many "); + buf.appendAscii( "elements in the initializer list, expected " ); + buf.append( state.getCntConsumed() ); + buf.appendAscii( ", got " ); + buf.append( (sal_Int32) PyTuple_Size(initializer) ); + throw RuntimeException( buf.makeStringAndClear(), Reference< XInterface > ()); } - ret = returnCandidate; + ret = PyRef( PyTuple_Pack(2, returnCandidate.get(), state.getUsed()), SAL_NO_ACQUIRE); } else { @@ -321,7 +421,7 @@ static PyObject *createUnoStructHelper(PyObject *, PyObject* args ) } else { - PyErr_SetString (PyExc_AttributeError, "1 Arguments: Structure Name"); + PyErr_SetString (PyExc_AttributeError, "pyuno._createUnoStructHelper: expects exactly two non-keyword arguments:\n\tStructure Name\n\tinitialiser tuple; may be the empty tuple"); } } catch( com::sun::star::uno::RuntimeException & e ) @@ -702,7 +802,7 @@ static PyObject *setCurrentContext( PyObject *, PyObject * args ) struct PyMethodDef PyUNOModule_methods [] = { {const_cast< char * >("getComponentContext"), getComponentContext, METH_VARARGS, NULL}, - {const_cast< char * >("_createUnoStructHelper"), createUnoStructHelper, METH_VARARGS | METH_KEYWORDS, NULL}, + {const_cast< char * >("_createUnoStructHelper"), reinterpret_cast(createUnoStructHelper), METH_VARARGS | METH_KEYWORDS, NULL}, {const_cast< char * >("getTypeByName"), getTypeByName, METH_VARARGS, NULL}, {const_cast< char * >("getConstantByName"), getConstantByName, METH_VARARGS, NULL}, {const_cast< char * >("getClass"), getClass, METH_VARARGS, NULL}, diff --git a/pyuno/source/module/uno.py b/pyuno/source/module/uno.py index bb9720568056..99fd0f04ab3a 100644 --- a/pyuno/source/module/uno.py +++ b/pyuno/source/module/uno.py @@ -4,6 +4,7 @@ # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # Copyright 2000, 2010 Oracle and/or its affiliates. +# 2011 Lionel Elie Mamane # # OpenOffice.org - a multi-platform office productivity suite # @@ -56,20 +57,23 @@ def getTypeByName( typeName): """ return pyuno.getTypeByName( typeName ) -def createUnoStruct( typeName, *args ): - """creates a uno struct or exception given by typeName. The parameter args may - 1) be empty. In this case, you get a default constructed uno structure. +def createUnoStruct( typeName, *args, **kwargs ): + """creates a uno struct or exception given by typeName. Can be called with: + 1) No additional argument. + In this case, you get a default constructed uno structure. ( e.g. createUnoStruct( "com.sun.star.uno.Exception" ) ) - 2) be a sequence with exactly one element, that contains an instance of typeName. + 2) Exactly one additional argument that is an instance of typeName. In this case, a copy constructed instance of typeName is returned ( e.g. createUnoStruct( "com.sun.star.uno.Exception" , e ) ) - 3) be a sequence, where the length of the sequence must match the number of - elements within typeName (e.g. - createUnoStruct( "com.sun.star.uno.Exception", "foo error" , self) ). The - elements with in the sequence must match the type of each struct element, - otherwise an exception is thrown. + 3) As many additional arguments as the number of elements within typeName + (e.g. createUnoStruct( "com.sun.star.uno.Exception", "foo error" , self) ). + 4) Keyword arguments to give values for each element of the struct by name. + 5) A mix of 3) and 4), such that each struct element is given a value exactly once, + either by a positional argument or by a keyword argument. + The additional and/or keyword arguments must match the type of each struct element, + otherwise an exception is thrown. """ - return getClass(typeName)( *args ) + return getClass(typeName)( *args, **kwargs ) def getClass( typeName ): """returns the class of a concrete uno exception, struct or interface @@ -310,11 +314,16 @@ def _impl_extractName(name): return name # private, referenced from the pyuno shared library -def _uno_struct__init__(self,*args): - if len(args) == 1 and hasattr(args[0], "__class__") and args[0].__class__ == self.__class__ : - self.__dict__["value"] = args[0] +def _uno_struct__init__(self,*args, **kwargs): + if len(kwargs) == 0 and len(args) == 1 and hasattr(args[0], "__class__") and args[0].__class__ == self.__class__ : + self.__dict__["value"] = args[0] else: - self.__dict__["value"] = pyuno._createUnoStructHelper(self.__class__.__pyunostruct__,args) + struct, used = pyuno._createUnoStructHelper(self.__class__.__pyunostruct__,args,**kwargs) + for kw in kwargs.keys(): + if not (kw in used and used[kw]): + RuntimeException = pyuno.getClass( "com.sun.star.uno.RuntimeException" ) + raise RuntimeException("_uno_struct__init__: unused keyword argument '" + kw + "'", None) + self.__dict__["value"] = struct # private, referenced from the pyuno shared library def _uno_struct__getattr__(self,name): -- cgit