summaryrefslogtreecommitdiffstats
path: root/pyuno/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /pyuno/source
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pyuno/source')
-rw-r--r--pyuno/source/loader/pythonloader.component26
-rw-r--r--pyuno/source/loader/pythonloader.py163
-rw-r--r--pyuno/source/loader/pyuno_loader.cxx277
-rw-r--r--pyuno/source/module/pyuno.cxx1715
-rw-r--r--pyuno/source/module/pyuno_adapter.cxx413
-rw-r--r--pyuno/source/module/pyuno_callable.cxx274
-rw-r--r--pyuno/source/module/pyuno_dlopenwrapper.c84
-rw-r--r--pyuno/source/module/pyuno_except.cxx232
-rw-r--r--pyuno/source/module/pyuno_gc.cxx132
-rw-r--r--pyuno/source/module/pyuno_impl.hxx302
-rw-r--r--pyuno/source/module/pyuno_iterator.cxx343
-rw-r--r--pyuno/source/module/pyuno_module.cxx896
-rw-r--r--pyuno/source/module/pyuno_runtime.cxx1012
-rw-r--r--pyuno/source/module/pyuno_struct.cxx397
-rw-r--r--pyuno/source/module/pyuno_type.cxx287
-rw-r--r--pyuno/source/module/pyuno_util.cxx212
-rw-r--r--pyuno/source/module/uno.py553
-rw-r--r--pyuno/source/module/unohelper.py297
-rw-r--r--pyuno/source/officehelper.py87
19 files changed, 7702 insertions, 0 deletions
diff --git a/pyuno/source/loader/pythonloader.component b/pyuno/source/loader/pythonloader.component
new file mode 100644
index 0000000000..4fc4a255ca
--- /dev/null
+++ b/pyuno/source/loader/pythonloader.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.comp.pyuno.Loader"
+ constructor="pyuno_Loader_get_implementation" single-instance="true">
+ <service name="com.sun.star.loader.Python"/>
+ </implementation>
+</component>
diff --git a/pyuno/source/loader/pythonloader.py b/pyuno/source/loader/pythonloader.py
new file mode 100644
index 0000000000..02f901942c
--- /dev/null
+++ b/pyuno/source/loader/pythonloader.py
@@ -0,0 +1,163 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+import uno
+import unohelper
+import sys
+import types
+import os
+from urllib.parse import unquote
+from com.sun.star.uno import Exception,RuntimeException
+from com.sun.star.loader import XImplementationLoader
+from com.sun.star.lang import XServiceInfo
+
+MODULE_PROTOCOL = "vnd.openoffice.pymodule:"
+DEBUG = 0
+
+g_supportedServices = "com.sun.star.loader.Python", # referenced by the native C++ loader !
+g_implementationName = "org.openoffice.comp.pyuno.Loader" # referenced by the native C++ loader !
+
+def splitUrl( url ):
+ nColon = url.find( ":" )
+ if -1 == nColon:
+ raise RuntimeException( "PythonLoader: No protocol in url " + url, None )
+ return url[0:nColon], url[nColon+1:len(url)]
+
+g_loadedComponents = {}
+def checkForPythonPathBesideComponent( url ):
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath.zip" );
+ if DEBUG == 1:
+ print(b"checking for existence of " + encfile( path ))
+ if 1 == os.access( encfile( path ), os.F_OK) and not path in sys.path:
+ if DEBUG == 1:
+ print(b"adding " + encfile( path ) + b" to sys.path")
+ sys.path.append( path )
+
+ path = unohelper.fileUrlToSystemPath( url+"/pythonpath" );
+ if 1 == os.access( encfile( path ), os.F_OK) and not path in sys.path:
+ if DEBUG == 1:
+ print(b"adding " + encfile( path ) + b" to sys.path")
+ sys.path.append( path )
+
+def encfile(uni):
+ return uni.encode( sys.getfilesystemencoding())
+
+class Loader( XImplementationLoader, XServiceInfo, unohelper.Base ):
+ def __init__(self, ctx ):
+ if DEBUG:
+ print("pythonloader.Loader ctor")
+ self.ctx = ctx
+
+ def getModuleFromUrl( self, url ):
+ if DEBUG:
+ print("pythonloader: interpreting url " + url)
+ protocol, dependent = splitUrl( url )
+ if "vnd.sun.star.expand" == protocol:
+ exp = self.ctx.getValueByName( "/singletons/com.sun.star.util.theMacroExpander" )
+ url = exp.expandMacros(unquote(dependent))
+ protocol,dependent = splitUrl( url )
+
+ if DEBUG:
+ print("pythonloader: after expansion " + protocol + ":" + dependent)
+
+ try:
+ if "file" == protocol:
+ # remove \..\ sequence, which may be useful e.g. in the build env
+ url = unohelper.absolutize( url, url )
+
+ # did we load the module already ?
+ mod = g_loadedComponents.get( url )
+ if not mod:
+ mod = types.ModuleType("uno_component")
+
+ # check for pythonpath.zip beside .py files
+ checkForPythonPathBesideComponent( url[0:url.rfind('/')] )
+
+ # read the file
+ filename = unohelper.fileUrlToSystemPath( url )
+
+ with open( filename, encoding='utf_8' ) as fileHandle:
+ src = fileHandle.read().replace("\r","")
+ if not src.endswith( "\n" ):
+ src = src + "\n"
+
+ # compile and execute the module
+ codeobject = compile( src, encfile(filename), "exec" )
+ mod.__file__ = filename
+ exec(codeobject, mod.__dict__)
+ g_loadedComponents[url] = mod
+ return mod
+ elif "vnd.openoffice.pymodule" == protocol:
+ nSlash = dependent.rfind('/')
+ if -1 != nSlash:
+ path = unohelper.fileUrlToSystemPath( dependent[0:nSlash] )
+ dependent = dependent[nSlash+1:len(dependent)]
+ if not path in sys.path:
+ sys.path.append( path )
+ mod = __import__( dependent )
+ path_component, dot, rest = dependent.partition('.')
+ while dot == '.':
+ path_component, dot, rest = rest.partition('.')
+ mod = getattr(mod, path_component)
+ return mod
+ else:
+ if DEBUG:
+ print("Unknown protocol '" + protocol + "'");
+ raise RuntimeException( "PythonLoader: Unknown protocol " +
+ protocol + " in url " +url, self )
+ except Exception as e:
+ if DEBUG:
+ print ("Python import exception " + str(type(e)) +
+ " message " + str(e) + " args " + str(e.args));
+ raise RuntimeException( "Couldn't load " + url + " for reason " + str(e), None )
+ return None
+
+ def activate( self, implementationName, dummy, locationUrl, regKey ):
+ if DEBUG:
+ print("pythonloader.Loader.activate")
+
+ mod = self.getModuleFromUrl( locationUrl )
+ implHelper = mod.__dict__.get( "g_ImplementationHelper" , None )
+ if DEBUG:
+ print ("Fetched ImplHelper as " + str(implHelper))
+ if implHelper is None:
+ return mod.getComponentFactory( implementationName, self.ctx.ServiceManager, regKey )
+ else:
+ return implHelper.getComponentFactory( implementationName,regKey,self.ctx.ServiceManager)
+
+ def writeRegistryInfo( self, regKey, dummy, locationUrl ):
+ if DEBUG:
+ print( "pythonloader.Loader.writeRegistryInfo" )
+
+ mod = self.getModuleFromUrl( locationUrl )
+ implHelper = mod.__dict__.get( "g_ImplementationHelper" , None )
+ if implHelper is None:
+ return mod.writeRegistryInfo( self.ctx.ServiceManager, regKey )
+ else:
+ return implHelper.writeRegistryInfo( regKey, self.ctx.ServiceManager )
+
+ def getImplementationName( self ):
+ return g_implementationName
+
+ def supportsService( self, ServiceName ):
+ return ServiceName in self.getSupportedServiceNames()
+
+ def getSupportedServiceNames( self ):
+ return g_supportedServices
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/source/loader/pyuno_loader.cxx b/pyuno/source/loader/pyuno_loader.cxx
new file mode 100644
index 0000000000..05a03fe72c
--- /dev/null
+++ b/pyuno/source/loader/pyuno_loader.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_folders.h>
+
+#include <pyuno.hxx>
+
+#include <o3tl/any.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <rtl/bootstrap.hxx>
+
+#include <cppuhelper/factory.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+// apparently PATH_MAX is not standard and not defined by MSVC
+#ifndef PATH_MAX
+#ifdef _MAX_PATH
+#define PATH_MAX _MAX_PATH
+#else
+#ifdef MAX_PATH
+#define PATH_MAX MAX_PATH
+#else
+#error no PATH_MAX
+#endif
+#endif
+#endif
+
+using pyuno::PyRef;
+using pyuno::NOT_NULL;
+using pyuno::Runtime;
+using pyuno::PyThreadAttach;
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::XComponentContext;
+using com::sun::star::uno::RuntimeException;
+
+namespace pyuno_loader
+{
+
+/// @throws RuntimeException
+static void raiseRuntimeExceptionWhenNeeded()
+{
+ if( PyErr_Occurred() )
+ {
+ PyRef excType, excValue, excTraceback;
+ PyErr_Fetch(reinterpret_cast<PyObject **>(&excType), reinterpret_cast<PyObject**>(&excValue), reinterpret_cast<PyObject**>(&excTraceback));
+ Runtime runtime;
+ css::uno::Any a = runtime.extractUnoException( excType, excValue, excTraceback );
+ OUStringBuffer buf( "python-loader:" );
+ if( auto e = o3tl::tryAccess<css::uno::Exception>(a) )
+ buf.append( e->Message );
+ throw RuntimeException( buf.makeStringAndClear() );
+ }
+}
+
+/// @throws RuntimeException
+static PyRef getLoaderModule()
+{
+ PyRef module(
+ PyImport_ImportModule( "pythonloader" ),
+ SAL_NO_ACQUIRE );
+ raiseRuntimeExceptionWhenNeeded();
+ if( !module.is() )
+ {
+ throw RuntimeException( "pythonloader: Couldn't load pythonloader module" );
+ }
+ return PyRef( PyModule_GetDict( module.get() ));
+}
+
+/// @throws RuntimeException
+static PyRef getObjectFromLoaderModule( const char * func )
+{
+ PyRef object( PyDict_GetItemString(getLoaderModule().get(), func ) );
+ if( !object.is() )
+ {
+ throw RuntimeException( "pythonloader: couldn't find core element pythonloader." +
+ OUString::createFromAscii( func ));
+ }
+ return object;
+}
+
+static void setPythonHome ( const OUString & pythonHome )
+{
+ OUString systemPythonHome;
+ osl_getSystemPathFromFileURL( pythonHome.pData, &(systemPythonHome.pData) );
+ // static because Py_SetPythonHome just copies the "wide" pointer
+ static wchar_t wide[PATH_MAX + 1];
+#if defined _WIN32
+ const size_t len = systemPythonHome.getLength();
+ if (len < std::size(wide))
+ wcsncpy(wide, o3tl::toW(systemPythonHome.getStr()), len + 1);
+#else
+ OString o = OUStringToOString(systemPythonHome, osl_getThreadTextEncoding());
+ size_t len = mbstowcs(wide, o.pData->buffer, PATH_MAX + 1);
+ if(len == size_t(-1))
+ {
+ PyErr_SetString(PyExc_SystemError, "invalid multibyte sequence in python home path");
+ return;
+ }
+#endif
+ if(len >= PATH_MAX + 1)
+ {
+ PyErr_SetString(PyExc_SystemError, "python home path is too long");
+ return;
+ }
+SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ Py_SetPythonHome(wide); // deprecated since python 3.11
+SAL_WNODEPRECATED_DECLARATIONS_POP
+}
+
+static void prependPythonPath( std::u16string_view pythonPathBootstrap )
+{
+ OUStringBuffer bufPYTHONPATH( 256 );
+ bool bAppendSep = false;
+ sal_Int32 nIndex = 0;
+ while( true )
+ {
+ size_t nNew = pythonPathBootstrap.find( ' ', nIndex );
+ std::u16string_view fileUrl;
+ if( nNew == std::u16string_view::npos )
+ {
+ fileUrl = pythonPathBootstrap.substr(nIndex);
+ }
+ else
+ {
+ fileUrl = pythonPathBootstrap.substr(nIndex, nNew - nIndex);
+ }
+ OUString systemPath;
+ osl_getSystemPathFromFileURL( OUString(fileUrl).pData, &(systemPath.pData) );
+ if (!systemPath.isEmpty())
+ {
+ if (bAppendSep)
+ bufPYTHONPATH.append(static_cast<sal_Unicode>(SAL_PATHSEPARATOR));
+ bufPYTHONPATH.append(systemPath);
+ bAppendSep = true;
+ }
+ if( nNew == std::u16string_view::npos )
+ break;
+ nIndex = nNew + 1;
+ }
+ const char * oldEnv = getenv( "PYTHONPATH");
+ if( oldEnv )
+ {
+ if (bAppendSep)
+ bufPYTHONPATH.append( static_cast<sal_Unicode>(SAL_PATHSEPARATOR) );
+ bufPYTHONPATH.append( OUString(oldEnv, strlen(oldEnv), osl_getThreadTextEncoding()) );
+ }
+
+ OUString envVar("PYTHONPATH");
+ OUString envValue(bufPYTHONPATH.makeStringAndClear());
+ osl_setEnvironment(envVar.pData, envValue.pData);
+}
+
+namespace {
+
+void pythonInit() {
+ if ( Py_IsInitialized()) // may be inited by getComponentContext() already
+ return;
+
+ OUString pythonPath;
+ OUString pythonHome;
+ OUString path( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("pythonloader.uno" ));
+ rtl::Bootstrap::expandMacros(path); //TODO: detect failure
+ rtl::Bootstrap bootstrap(path);
+
+ // look for pythonhome
+ bootstrap.getFrom( "PYUNO_LOADER_PYTHONHOME", pythonHome );
+ bootstrap.getFrom( "PYUNO_LOADER_PYTHONPATH", pythonPath );
+
+ // pythonhome+pythonpath must be set before Py_Initialize(), otherwise there appear warning on the console
+ // sadly, there is no api for setting the pythonpath, we have to use the environment variable
+ if( !pythonHome.isEmpty() )
+ setPythonHome( pythonHome );
+
+ if( !pythonPath.isEmpty() )
+ prependPythonPath( pythonPath );
+
+#ifdef _WIN32
+ //extend PATH under windows to include the branddir/program so ssl libs will be found
+ //for use by terminal mailmerge dependency _ssl.pyd
+ OUString sEnvName("PATH");
+ OUString sPath;
+ osl_getEnvironment(sEnvName.pData, &sPath.pData);
+ OUString sBrandLocation("$BRAND_BASE_DIR/program");
+ rtl::Bootstrap::expandMacros(sBrandLocation);
+ osl::FileBase::getSystemPathFromFileURL(sBrandLocation, sBrandLocation);
+ sPath = sPath + OUStringChar(SAL_PATHSEPARATOR) + sBrandLocation;
+ osl_setEnvironment(sEnvName.pData, sPath.pData);
+#endif
+
+ PyImport_AppendInittab( "pyuno", PyInit_pyuno );
+
+#if HAVE_FEATURE_READONLY_INSTALLSET
+ Py_DontWriteBytecodeFlag = 1;
+#endif
+
+ // initialize python
+ Py_Initialize();
+#if PY_VERSION_HEX < 0x03090000
+ PyEval_InitThreads();
+#endif
+
+ PyThreadState *tstate = PyThreadState_Get();
+ PyEval_ReleaseThread( tstate );
+#if PY_VERSION_HEX < 0x030B0000
+ // This tstate is never used again, so delete it here.
+ // This prevents an assertion in PyThreadState_Swap on the
+ // PyThreadAttach below.
+ PyThreadState_Delete(tstate);
+#endif
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+pyuno_Loader_get_implementation(
+ css::uno::XComponentContext* ctx , css::uno::Sequence<css::uno::Any> const&)
+{
+ // tdf#114815 init python only once, via single-instace="true" in pythonloader.component
+ pythonInit();
+
+ Reference< XInterface > ret;
+
+ PyThreadAttach attach( PyInterpreterState_Head() );
+ {
+ // note: this can't race against getComponentContext() because
+ // either (in soffice.bin) CreateInstance() must be called before
+ // getComponentContext() can be called, or (in python.bin) the other
+ // way around
+ if( ! Runtime::isInitialized() )
+ {
+ Runtime::initialize( ctx );
+ }
+ Runtime runtime;
+
+ PyRef pyCtx = runtime.any2PyObject(
+ css::uno::Any( css::uno::Reference(ctx) ) );
+
+ PyRef clazz = getObjectFromLoaderModule( "Loader" );
+ PyRef args ( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem( args.get(), 0 , pyCtx.getAcquired() );
+ PyRef pyInstance( PyObject_CallObject( clazz.get() , args.get() ), SAL_NO_ACQUIRE );
+ runtime.pyObject2Any( pyInstance ) >>= ret;
+ }
+ ret->acquire();
+ return ret.get();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno.cxx b/pyuno/source/module/pyuno.cxx
new file mode 100644
index 0000000000..bd4904d5f7
--- /dev/null
+++ b/pyuno/source/module/pyuno.cxx
@@ -0,0 +1,1715 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+
+#include <rtl/ustrbuf.hxx>
+
+#include <typelib/typedescription.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/script/XTypeConverter.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/servicehelper.hxx>
+
+#include "pyuno_impl.hxx"
+
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::Type;
+using com::sun::star::uno::TypeClass;
+using com::sun::star::uno::TypeDescription;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Exception;
+using com::sun::star::lang::XSingleServiceFactory;
+using com::sun::star::lang::XServiceInfo;
+using com::sun::star::lang::XTypeProvider;
+using com::sun::star::lang::XUnoTunnel;
+using com::sun::star::script::XInvocation2;
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XEnumerationAccess;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::container::XIndexContainer;
+using com::sun::star::container::XIndexReplace;
+using com::sun::star::container::XNameAccess;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::container::XNameReplace;
+
+namespace pyuno
+{
+
+static PyObject *PyUNO_str( PyObject * self );
+
+static void PyUNO_del (PyObject* self)
+{
+ PyUNO* me = reinterpret_cast< PyUNO* > (self);
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del (self);
+}
+
+
+OUString val2str( const void * pVal, typelib_TypeDescriptionReference * pTypeRef , sal_Int32 mode )
+{
+ assert( pVal );
+ if (pTypeRef->eTypeClass == typelib_TypeClass_VOID)
+ return "void";
+
+ OUStringBuffer buf( 64 );
+ buf.append( "(" + OUString::unacquired(&pTypeRef->pTypeName) + ")" );
+
+ switch (pTypeRef->eTypeClass)
+ {
+ case typelib_TypeClass_INTERFACE:
+ {
+ buf.append( "0x" +
+ OUString::number( reinterpret_cast< sal_IntPtr >(*static_cast<void * const *>(pVal)), 16 ));
+ if( VAL2STR_MODE_DEEP == mode )
+ {
+ buf.append( "{" ); Reference< XInterface > r = *static_cast<Reference< XInterface > const *>(pVal);
+ Reference< XServiceInfo > serviceInfo( r, UNO_QUERY);
+ Reference< XTypeProvider > typeProvider(r,UNO_QUERY);
+ if( serviceInfo.is() )
+ {
+ buf.append("implementationName="
+ + serviceInfo->getImplementationName()
+ + ", supportedServices={" );
+ Sequence< OUString > seq = serviceInfo->getSupportedServiceNames();
+ for( int i = 0 ; i < seq.getLength() ; i ++ )
+ {
+ buf.append( seq[i] );
+ if( i +1 != seq.getLength() )
+ buf.append( "," );
+ }
+ buf.append("}");
+ }
+
+ if( typeProvider.is() )
+ {
+ buf.append(", supportedInterfaces={" );
+ Sequence< Type > seq (typeProvider->getTypes());
+ for( int i = 0 ; i < seq.getLength() ; i ++ )
+ {
+ buf.append(seq[i].getTypeName());
+ if( i +1 != seq.getLength() )
+ buf.append( "," );
+ }
+ buf.append("}");
+ }
+ buf.append( "}" );
+ }
+
+ break;
+ }
+ case typelib_TypeClass_STRUCT:
+ case typelib_TypeClass_EXCEPTION:
+ {
+ buf.append( "{ " );
+ typelib_TypeDescription * pTypeDescr = nullptr;
+ TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
+ assert( pTypeDescr );
+
+ typelib_CompoundTypeDescription * pCompType = reinterpret_cast<typelib_CompoundTypeDescription *>(pTypeDescr);
+ sal_Int32 nDescr = pCompType->nMembers;
+
+ if (pCompType->pBaseTypeDescription)
+ {
+ buf.append( val2str( pVal, pCompType->pBaseTypeDescription->aBase.pWeakRef, mode ) );
+ if (nDescr)
+ buf.append( ", " );
+ }
+
+ typelib_TypeDescriptionReference ** ppTypeRefs = pCompType->ppTypeRefs;
+ sal_Int32 * pMemberOffsets = pCompType->pMemberOffsets;
+ rtl_uString ** ppMemberNames = pCompType->ppMemberNames;
+
+ for ( sal_Int32 nPos = 0; nPos < nDescr; ++nPos )
+ {
+ buf.append( OUString::unacquired(&ppMemberNames[nPos]) + " = " );
+ typelib_TypeDescription * pMemberType = nullptr;
+ TYPELIB_DANGER_GET( &pMemberType, ppTypeRefs[nPos] );
+ buf.append( val2str( static_cast<char const *>(pVal) + pMemberOffsets[nPos], pMemberType->pWeakRef, mode ) );
+ TYPELIB_DANGER_RELEASE( pMemberType );
+ if (nPos < (nDescr -1))
+ buf.append( ", " );
+ }
+
+ TYPELIB_DANGER_RELEASE( pTypeDescr );
+
+ buf.append( " }" );
+ break;
+ }
+ case typelib_TypeClass_SEQUENCE:
+ {
+ typelib_TypeDescription * pTypeDescr = nullptr;
+ TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
+
+ uno_Sequence * pSequence = *static_cast<uno_Sequence * const *>(pVal);
+ typelib_TypeDescription * pElementTypeDescr = nullptr;
+ TYPELIB_DANGER_GET( &pElementTypeDescr, reinterpret_cast<typelib_IndirectTypeDescription *>(pTypeDescr)->pType );
+
+ sal_Int32 nElementSize = pElementTypeDescr->nSize;
+ sal_Int32 nElements = pSequence->nElements;
+
+ if (nElements)
+ {
+ buf.append( "{ " );
+ char * pElements = pSequence->elements;
+ for ( sal_Int32 nPos = 0; nPos < nElements; ++nPos )
+ {
+ buf.append( val2str( pElements + (nElementSize * nPos), pElementTypeDescr->pWeakRef, mode ) );
+ if (nPos < (nElements -1))
+ buf.append( ", " );
+ }
+ buf.append( " }" );
+ }
+ else
+ {
+ buf.append( "{}" );
+ }
+ TYPELIB_DANGER_RELEASE( pElementTypeDescr );
+ TYPELIB_DANGER_RELEASE( pTypeDescr );
+ break;
+ }
+ case typelib_TypeClass_ANY:
+ buf.append( "{ "
+ + val2str( static_cast<uno_Any const *>(pVal)->pData,
+ static_cast<uno_Any const *>(pVal)->pType ,
+ mode)
+ + " }" );
+ break;
+ case typelib_TypeClass_TYPE:
+ buf.append( (*static_cast<typelib_TypeDescriptionReference * const *>(pVal))->pTypeName );
+ break;
+ case typelib_TypeClass_STRING:
+ buf.append( "\"" +
+ OUString::unacquired(&*static_cast<rtl_uString * const *>(pVal)) +
+ "\"" );
+ break;
+ case typelib_TypeClass_ENUM:
+ {
+ typelib_TypeDescription * pTypeDescr = nullptr;
+ TYPELIB_DANGER_GET( &pTypeDescr, pTypeRef );
+
+ sal_Int32 * pValues = reinterpret_cast<typelib_EnumTypeDescription *>(pTypeDescr)->pEnumValues;
+ sal_Int32 nPos = reinterpret_cast<typelib_EnumTypeDescription *>(pTypeDescr)->nEnumValues;
+ while (nPos--)
+ {
+ if (pValues[nPos] == *static_cast<int const *>(pVal))
+ break;
+ }
+ if (nPos >= 0)
+ buf.append( reinterpret_cast<typelib_EnumTypeDescription *>(pTypeDescr)->ppEnumNames[nPos] );
+ else
+ buf.append( '?' );
+
+ TYPELIB_DANGER_RELEASE( pTypeDescr );
+ break;
+ }
+ case typelib_TypeClass_BOOLEAN:
+ if (*static_cast<sal_Bool const *>(pVal))
+ buf.append( "true" );
+ else
+ buf.append( "false" );
+ break;
+ case typelib_TypeClass_CHAR:
+ buf.append( "\'"
+ + OUStringChar(*static_cast<sal_Unicode const *>(pVal) )
+ + "\'" );
+ break;
+ case typelib_TypeClass_FLOAT:
+ buf.append( *static_cast<float const *>(pVal) );
+ break;
+ case typelib_TypeClass_DOUBLE:
+ buf.append( *static_cast<double const *>(pVal) );
+ break;
+ case typelib_TypeClass_BYTE:
+ buf.append( "0x" +
+ OUString::number( static_cast<sal_Int32>(*static_cast<sal_Int8 const *>(pVal)), 16 ));
+ break;
+ case typelib_TypeClass_SHORT:
+ buf.append( "0x" +
+ OUString::number( static_cast<sal_Int32>(*static_cast<sal_Int16 const *>(pVal)), 16 ));
+ break;
+ case typelib_TypeClass_UNSIGNED_SHORT:
+ buf.append( "0x" +
+ OUString::number( static_cast<sal_Int32>(*static_cast<sal_uInt16 const *>(pVal)), 16 ));
+ break;
+ case typelib_TypeClass_LONG:
+ buf.append( "0x" +
+ OUString::number( *static_cast<sal_Int32 const *>(pVal), 16 ));
+ break;
+ case typelib_TypeClass_UNSIGNED_LONG:
+ buf.append( "0x" +
+ OUString::number( static_cast<sal_Int64>(*static_cast<sal_uInt32 const *>(pVal)), 16 ));
+ break;
+ case typelib_TypeClass_HYPER:
+ case typelib_TypeClass_UNSIGNED_HYPER:
+ buf.append( "0x" );
+#if defined(__GNUC__) && defined(SPARC)
+// I guess this really should check if there are strict alignment
+// requirements, not just "GCC on SPARC".
+ {
+ sal_Int64 aVal;
+ *(sal_Int32 *)&aVal = *(sal_Int32 *)pVal;
+ *((sal_Int32 *)&aVal +1)= *((sal_Int32 *)pVal +1);
+ buf.append( aVal, 16 );
+ }
+#else
+ buf.append( *static_cast<sal_Int64 const *>(pVal), 16 );
+#endif
+ break;
+
+ case typelib_TypeClass_VOID:
+ case typelib_TypeClass_UNKNOWN:
+ case typelib_TypeClass_SERVICE:
+ case typelib_TypeClass_MODULE:
+ default:
+ buf.append( '?' );
+ }
+
+ return buf.makeStringAndClear();
+}
+
+static sal_Int32 lcl_PyNumber_AsSal_Int32( PyObject *pObj )
+{
+ // Check object is an index
+ PyRef rIndex( PyNumber_Index( pObj ), SAL_NO_ACQUIRE );
+ if ( !rIndex.is() )
+ return -1;
+
+ // Convert Python number to platform long, then check actual value against
+ // bounds of sal_Int32
+ int nOverflow;
+ long nResult = PyLong_AsLongAndOverflow( pObj, &nOverflow );
+ if ( nOverflow || nResult > SAL_MAX_INT32 || nResult < SAL_MIN_INT32) {
+ PyErr_SetString( PyExc_IndexError, "Python int too large to convert to UNO long" );
+ return -1;
+ }
+
+ return nResult;
+}
+
+static int lcl_PySlice_GetIndicesEx( PyObject *pObject, sal_Int32 nLen, sal_Int32 *nStart, sal_Int32 *nStop, sal_Int32 *nStep, sal_Int32 *nSliceLength )
+{
+ Py_ssize_t nStart_ssize, nStop_ssize, nStep_ssize, nSliceLength_ssize;
+
+ int nResult = PySlice_GetIndicesEx(pObject,
+ nLen, &nStart_ssize, &nStop_ssize, &nStep_ssize, &nSliceLength_ssize );
+ if (nResult == -1)
+ return -1;
+
+ if ( nStart_ssize > SAL_MAX_INT32 || nStart_ssize < SAL_MIN_INT32
+ || nStop_ssize > SAL_MAX_INT32 || nStop_ssize < SAL_MIN_INT32
+ || nStep_ssize > SAL_MAX_INT32 || nStep_ssize < SAL_MIN_INT32
+ || nSliceLength_ssize > SAL_MAX_INT32 || nSliceLength_ssize < SAL_MIN_INT32 )
+ {
+ PyErr_SetString( PyExc_IndexError, "Python int too large to convert to UNO long" );
+ return -1;
+ }
+
+ *nStart = static_cast<sal_Int32>(nStart_ssize);
+ *nStop = static_cast<sal_Int32>(nStop_ssize);
+ *nStep = static_cast<sal_Int32>(nStep_ssize);
+ *nSliceLength = static_cast<sal_Int32>(nSliceLength_ssize);
+ return 0;
+}
+
+static bool lcl_hasInterfaceByName( Any const &object, OUString const & interfaceName )
+{
+ Reference< XInterface > xInterface( object, UNO_QUERY );
+ TypeDescription typeDesc( interfaceName );
+ Any aInterface = xInterface->queryInterface( typeDesc.get()->pWeakRef );
+
+ return aInterface.hasValue();
+}
+
+static PyObject *PyUNO_repr( PyObject * self )
+{
+ return PyUNO_str( self );
+}
+
+static Py_hash_t PyUNO_hash( PyObject *self )
+{
+
+ PyUNO *me = reinterpret_cast<PyUNO *>(self);
+
+ // Py_hash_t is not necessarily the same size as a pointer, but this is not
+ // important for hashing - it just has to return the same value each time
+ return sal::static_int_cast< Py_hash_t >( reinterpret_cast< sal_IntPtr > (
+ *static_cast<void * const *>(me->members->wrappedObject.getValue()) ) );
+
+}
+
+PyObject *PyUNO_invoke( PyObject *object, const char *name , PyObject *args )
+{
+ PyRef ret;
+ try
+ {
+ Runtime runtime;
+
+ PyRef paras,callable;
+ if( PyObject_IsInstance( object, getPyUnoClass().get() ) )
+ {
+ PyUNO* me = reinterpret_cast<PyUNO*>(object);
+ OUString attrName = OUString::createFromAscii(name);
+ if (! me->members->xInvocation->hasMethod (attrName))
+ {
+ throw RuntimeException( "Attribute " + attrName + " unknown" );
+ }
+ callable = PyUNO_callable_new (
+ me->members->xInvocation,
+ attrName,
+ ACCEPT_UNO_ANY);
+ paras = args;
+ }
+ else
+ {
+ // clean the tuple from uno.Any !
+ int size = PyTuple_Size( args );
+ { // for CC, keeping ref-count of tuple being 1
+ paras = PyRef(PyTuple_New( size ), SAL_NO_ACQUIRE);
+ }
+ for( int i = 0 ; i < size ;i ++ )
+ {
+ PyObject * element = PyTuple_GetItem( args , i );
+ if( PyObject_IsInstance( element , getAnyClass( runtime ).get() ) )
+ {
+ element = PyObject_GetAttrString(
+ element, "value" );
+ }
+ else
+ {
+ Py_XINCREF( element );
+ }
+ PyTuple_SetItem( paras.get(), i , element );
+ }
+ callable = PyRef( PyObject_GetAttrString( object , name ), SAL_NO_ACQUIRE );
+ if( !callable.is() )
+ return nullptr;
+ }
+ ret = PyRef( PyObject_CallObject( callable.get(), paras.get() ), SAL_NO_ACQUIRE );
+ }
+ catch (const css::lang::IllegalArgumentException &e)
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch (const css::script::CannotConvertException &e)
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch (const css::uno::RuntimeException &e)
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch (const css::uno::Exception &e)
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return ret.getAcquired();
+}
+
+PyObject *PyUNO_str( PyObject * self )
+{
+ PyUNO *me = reinterpret_cast<PyUNO *>(self);
+
+ OString buf;
+
+ {
+ PyThreadDetach antiguard;
+
+ OUString s = val2str( me->members->wrappedObject.getValue(),
+ me->members->wrappedObject.getValueType().getTypeLibType() );
+ buf = "pyuno object " + OUStringToOString(s,RTL_TEXTENCODING_ASCII_US);
+ }
+
+ return PyUnicode_FromString( buf.getStr() );
+}
+
+static PyObject* PyUNO_dir (PyObject* self)
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ PyObject* member_list = nullptr;
+ Sequence<OUString> oo_member_list;
+
+ try
+ {
+ oo_member_list = me->members->xInvocation->getMemberNames ();
+ member_list = PyList_New (oo_member_list.getLength ());
+ for (int i = 0; i < oo_member_list.getLength (); i++)
+ {
+ // setitem steals a reference
+ PyList_SetItem (member_list, i, ustring2PyString(oo_member_list[i]).getAcquired() );
+ }
+ }
+ catch( const RuntimeException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+
+ return member_list;
+}
+
+static sal_Int32 lcl_detach_getLength( PyUNO const *me )
+{
+ PyThreadDetach antiguard;
+
+ // If both XIndexContainer and XNameContainer are implemented, it is
+ // assumed that getCount() gives the same result as the number of names
+ // returned by getElementNames(), or the user may be surprised.
+
+ // For XIndexContainer
+ Reference< XIndexAccess > xIndexAccess( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ {
+ return xIndexAccess->getCount();
+ }
+
+ // For XNameContainer
+ // Not terribly efficient - get the count of all the names
+ Reference< XNameAccess > xNameAccess( me->members->xInvocation, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ return xNameAccess->getElementNames().getLength();
+ }
+
+ return -1;
+}
+
+static int PyUNO_bool( PyObject* self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ int nLen = lcl_detach_getLength( me );
+ if (nLen >= 0)
+ return nLen == 0 ? 0 : 1;
+
+ // Anything which doesn't have members is a scalar object and therefore true
+ return 1;
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return -1;
+}
+
+static Py_ssize_t PyUNO_len( PyObject* self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ int nLen = lcl_detach_getLength( me );
+ if (nLen >= 0)
+ return nLen;
+
+ PyErr_SetString( PyExc_TypeError, "object has no len()" );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return -1;
+}
+
+static void lcl_getRowsColumns( PyUNO const * me, sal_Int32& nRows, sal_Int32& nColumns )
+{
+ Sequence<short> aOutParamIndex;
+ Sequence<Any> aOutParam;
+ Sequence<Any> aParams;
+ Any aRet = me->members->xInvocation->invoke ( "getRows", aParams, aOutParamIndex, aOutParam );
+ Reference< XIndexAccess > xIndexAccessRows( aRet, UNO_QUERY );
+ nRows = xIndexAccessRows->getCount();
+ aRet = me->members->xInvocation->invoke ( "getColumns", aParams, aOutParamIndex, aOutParam );
+ Reference< XIndexAccess > xIndexAccessCols( aRet, UNO_QUERY );
+ nColumns = xIndexAccessCols->getCount();
+}
+
+static PyRef lcl_indexToSlice( const PyRef& rIndex )
+{
+ Py_ssize_t nIndex = PyNumber_AsSsize_t( rIndex.get(), PyExc_IndexError );
+ if (nIndex == -1 && PyErr_Occurred())
+ return nullptr;
+ PyRef rStart( PyLong_FromSsize_t( nIndex ), SAL_NO_ACQUIRE );
+ PyRef rStop( PyLong_FromSsize_t( nIndex+1 ), SAL_NO_ACQUIRE );
+ PyRef rStep( PyLong_FromLong( 1 ), SAL_NO_ACQUIRE );
+ PyRef rSlice( PySlice_New( rStart.get(), rStop.get(), rStep.get() ), SAL_NO_ACQUIRE );
+
+ return rSlice;
+}
+
+static PyObject* lcl_getitem_XCellRange( PyUNO const * me, PyObject* pKey )
+{
+ Runtime runtime;
+
+ Sequence<short> aOutParamIndex;
+ Sequence<Any> aOutParam;
+ Sequence<Any> aParams;
+ Any aRet;
+
+ // Single string key is sugar for getCellRangeByName()
+ if ( PyUnicode_Check( pKey ) ) {
+
+ aParams = { Any(pyString2ustring( pKey )) };
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellRangeByName", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject ( aRet );
+ return rRet.getAcquired();
+
+ }
+
+ PyRef rKey0, rKey1;
+ if ( PyIndex_Check( pKey ) )
+ {
+ // [0] is equivalent to [0,:]
+ rKey0 = pKey;
+ rKey1 = PySlice_New( nullptr, nullptr, nullptr );
+ }
+ else if ( PyTuple_Check( pKey ) && (PyTuple_Size( pKey ) == 2) )
+ {
+ rKey0 = PyTuple_GetItem( pKey, 0 );
+ rKey1 = PyTuple_GetItem( pKey, 1 );
+ }
+ else
+ {
+ PyErr_SetString( PyExc_KeyError, "invalid subscript" );
+ return nullptr;
+ }
+
+ // If both keys are indices, return the corresponding cell
+ if ( PyIndex_Check( rKey0.get() ) && PyIndex_Check( rKey1.get() ))
+ {
+ sal_Int32 nKey0_s = lcl_PyNumber_AsSal_Int32( rKey0.get() );
+ sal_Int32 nKey1_s = lcl_PyNumber_AsSal_Int32( rKey1.get() );
+
+ if ( ((nKey0_s == -1) || (nKey1_s == -1)) && PyErr_Occurred() )
+ return nullptr;
+
+ aParams = { Any(nKey1_s), Any(nKey0_s) };
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellByPosition", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ // If either argument is an index, coerce it to a slice
+ if ( PyIndex_Check( rKey0.get() ) )
+ rKey0 = lcl_indexToSlice( rKey0 );
+
+ if ( PyIndex_Check( rKey1.get() ) )
+ rKey1 = lcl_indexToSlice( rKey1 );
+
+ // If both arguments are slices, return the corresponding cell range
+ if ( PySlice_Check( rKey0.get() ) && PySlice_Check( rKey1.get() ) )
+ {
+ sal_Int32 nLen0 = SAL_MAX_INT32, nLen1 = SAL_MAX_INT32;
+ sal_Int32 nStart0 = 0, nStop0 = 0, nStep0 = 0, nSliceLength0 = 0;
+ sal_Int32 nStart1 = 0, nStop1 = 0, nStep1 = 0, nSliceLength1 = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ if ( lcl_hasInterfaceByName( me->members->wrappedObject, "com.sun.star.table.XColumnRowRange" ) )
+ {
+ lcl_getRowsColumns (me, nLen0, nLen1);
+ }
+ }
+
+ int nSuccess1 = lcl_PySlice_GetIndicesEx( rKey0.get(), nLen0, &nStart0, &nStop0, &nStep0, &nSliceLength0 );
+ int nSuccess2 = lcl_PySlice_GetIndicesEx( rKey1.get(), nLen1, &nStart1, &nStop1, &nStep1, &nSliceLength1 );
+ if ( ((nSuccess1 == -1) || (nSuccess2 == -1)) && PyErr_Occurred() )
+ return nullptr;
+
+ if ( nSliceLength0 <= 0 || nSliceLength1 <= 0 )
+ {
+ PyErr_SetString( PyExc_KeyError, "invalid number of rows or columns" );
+ return nullptr;
+ }
+
+ if ( nStep0 == 1 && nStep1 == 1 )
+ {
+ aParams = { Any(nStart1), Any(nStart0), Any(nStop1 - 1), Any(nStop0 - 1) };
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellRangeByPosition", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ PyErr_SetString( PyExc_KeyError, "step != 1 not supported" );
+ return nullptr;
+ }
+
+ PyErr_SetString( PyExc_KeyError, "invalid subscript" );
+ return nullptr;
+}
+
+static PyObject* lcl_getitem_index( PyUNO const *me, PyObject *pKey, Runtime const & runtime )
+{
+ Any aRet;
+ sal_Int32 nIndex;
+
+ nIndex = lcl_PyNumber_AsSal_Int32( pKey );
+ if (nIndex == -1 && PyErr_Occurred())
+ return nullptr;
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XIndexAccess > xIndexAccess( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ {
+ if (nIndex < 0)
+ nIndex += xIndexAccess->getCount();
+ aRet = xIndexAccess->getByIndex( nIndex );
+ }
+ }
+ if ( aRet.hasValue() )
+ {
+ PyRef rRet ( runtime.any2PyObject( aRet ) );
+ return rRet.getAcquired();
+ }
+
+ return nullptr;
+}
+
+static PyObject* lcl_getitem_slice( PyUNO const *me, PyObject *pKey )
+{
+ Runtime runtime;
+
+ Reference< XIndexAccess > xIndexAccess;
+ sal_Int32 nLen = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexAccess.set( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ nLen = xIndexAccess->getCount();
+ }
+
+ if ( !xIndexAccess )
+ return nullptr;
+
+ sal_Int32 nStart = 0, nStop = 0, nStep = 0, nSliceLength = 0;
+ int nSuccess = lcl_PySlice_GetIndicesEx(pKey, nLen, &nStart, &nStop, &nStep, &nSliceLength);
+ if ( nSuccess == -1 && PyErr_Occurred() )
+ return nullptr;
+
+ PyRef rTuple( PyTuple_New( nSliceLength ), SAL_NO_ACQUIRE, NOT_NULL );
+ sal_Int32 nCur, i;
+ for ( nCur = nStart, i = 0; i < nSliceLength; nCur += nStep, i++ )
+ {
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+
+ aRet = xIndexAccess->getByIndex( nCur );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ PyTuple_SetItem( rTuple.get(), i, rRet.getAcquired() );
+ }
+
+ return rTuple.getAcquired();
+}
+
+static PyObject* lcl_getitem_string( PyUNO const *me, PyObject *pKey, Runtime const & runtime )
+{
+ OUString sKey = pyString2ustring( pKey );
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XNameAccess > xNameAccess( me->members->xInvocation, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ aRet = xNameAccess->getByName( sKey );
+ }
+ }
+ if ( aRet.hasValue() )
+ {
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ return nullptr;
+}
+
+static PyObject* PyUNO_getitem( PyObject *self, PyObject *pKey )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+ Runtime runtime;
+
+ try
+ {
+ // XIndexAccess access by index
+ if ( PyIndex_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_index( me, pKey, runtime );
+ if ( pRet != nullptr || PyErr_Occurred() )
+ return pRet;
+ }
+
+ // XIndexAccess access by slice
+ if ( PySlice_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_slice( me, pKey );
+ if ( pRet != nullptr || PyErr_Occurred() )
+ return pRet;
+ }
+
+ // XNameAccess access by key
+ if ( PyUnicode_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_string( me, pKey, runtime );
+ if ( pRet != nullptr )
+ return pRet;
+ }
+
+ // XCellRange/XColumnRowRange specialisation
+ // Uses reflection as we can't have a hard dependency on XCellRange here
+ bool hasXCellRange = false;
+
+ {
+ PyThreadDetach antiguard;
+
+ hasXCellRange = lcl_hasInterfaceByName( me->members->wrappedObject, "com.sun.star.table.XCellRange" );
+ }
+ if ( hasXCellRange )
+ {
+ return lcl_getitem_XCellRange( me, pKey );
+ }
+
+
+ // If the object is an XIndexAccess and/or XNameAccess, but the
+ // key passed wasn't suitable, give a TypeError which specifically
+ // describes this
+ Reference< XIndexAccess > xIndexAccess( me->members->xInvocation, UNO_QUERY );
+ Reference< XNameAccess > xNameAccess( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexAccess.is() || xNameAccess.is() )
+ {
+ PyErr_SetString( PyExc_TypeError, "subscription with invalid type" );
+ return nullptr;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "object is not subscriptable" );
+ }
+ catch( const css::lang::IndexOutOfBoundsException & )
+ {
+ PyErr_SetString( PyExc_IndexError, "index out of range" );
+ }
+ catch( const css::container::NoSuchElementException & )
+ {
+ PyErr_SetString( PyExc_KeyError, "key not found" );
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return nullptr;
+}
+
+static int lcl_setitem_index( PyUNO const *me, PyObject *pKey, PyObject *pValue )
+{
+ Runtime runtime;
+
+ Reference< XIndexContainer > xIndexContainer;
+ Reference< XIndexReplace > xIndexReplace;
+ sal_Int32 nIndex = lcl_PyNumber_AsSal_Int32( pKey );
+ if ( nIndex == -1 && PyErr_Occurred() )
+ return 0;
+
+ bool isTuple = false;
+
+ Any aValue;
+ if ( pValue != nullptr )
+ {
+ isTuple = PyTuple_Check( pValue );
+
+ try
+ {
+ aValue = runtime.pyObject2Any( pValue );
+ }
+ catch ( const css::uno::RuntimeException & )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw css::script::CannotConvertException();
+ }
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer.set( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexContainer.is() )
+ xIndexReplace = xIndexContainer;
+ else
+ xIndexReplace.set( me->members->xInvocation, UNO_QUERY );
+
+ if ( xIndexReplace.is() && nIndex < 0 )
+ nIndex += xIndexReplace->getCount();
+
+ // XIndexReplace replace by index
+ if ( (pValue != nullptr) && xIndexReplace.is() )
+ {
+ if ( isTuple )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xIndexReplace->getElementType();
+ aValue = runtime.getImpl()->cargo->xTypeConverter->convertTo( aValue, aType );
+ }
+
+ xIndexReplace->replaceByIndex( nIndex, aValue );
+ return 0;
+ }
+
+ // XIndexContainer remove by index
+ if ( (pValue == nullptr) && xIndexContainer.is() )
+ {
+ xIndexContainer->removeByIndex( nIndex );
+ return 0;
+ }
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+static int lcl_setitem_slice( PyUNO const *me, PyObject *pKey, PyObject *pValue )
+{
+ // XIndexContainer insert/remove/replace by slice
+ Runtime runtime;
+
+ Reference< XIndexReplace > xIndexReplace;
+ Reference< XIndexContainer > xIndexContainer;
+ sal_Int32 nLen = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer.set( me->members->xInvocation, UNO_QUERY );
+ if ( xIndexContainer.is() )
+ xIndexReplace = xIndexContainer;
+ else
+ xIndexReplace.set( me->members->xInvocation, UNO_QUERY );
+
+ if ( xIndexReplace.is() )
+ nLen = xIndexReplace->getCount();
+ }
+
+ if ( xIndexReplace.is() )
+ {
+ sal_Int32 nStart = 0, nStop = 0, nStep = 0, nSliceLength = 0;
+ int nSuccess = lcl_PySlice_GetIndicesEx( pKey, nLen, &nStart, &nStop, &nStep, &nSliceLength );
+ if ( (nSuccess == -1) && PyErr_Occurred() )
+ return 0;
+
+ if ( pValue == nullptr )
+ {
+ pValue = PyTuple_New( 0 );
+ }
+
+ if ( !PyTuple_Check (pValue) )
+ {
+ PyErr_SetString( PyExc_TypeError, "value is not a tuple" );
+ return 1;
+ }
+
+ Py_ssize_t nTupleLength_ssize = PyTuple_Size( pValue );
+ if ( nTupleLength_ssize > SAL_MAX_INT32 )
+ {
+ PyErr_SetString( PyExc_ValueError, "tuple too large" );
+ return 1;
+ }
+ sal_Int32 nTupleLength = static_cast<sal_Int32>(nTupleLength_ssize);
+
+ if ( (nTupleLength != nSliceLength) && (nStep != 1) )
+ {
+ PyErr_SetString( PyExc_ValueError, "number of items assigned must be equal" );
+ return 1;
+ }
+
+ if ( (nTupleLength != nSliceLength) && !xIndexContainer.is() )
+ {
+ PyErr_SetString( PyExc_ValueError, "cannot change length" );
+ return 1;
+ }
+
+ sal_Int32 nCur, i;
+ sal_Int32 nMax = ::std::max( nSliceLength, nTupleLength );
+ for ( nCur = nStart, i = 0; i < nMax; nCur += nStep, i++ )
+ {
+ if ( i < nTupleLength )
+ {
+ PyRef rItem = PyTuple_GetItem( pValue, i );
+ bool isTuple = PyTuple_Check( rItem.get() );
+
+ Any aItem;
+ try
+ {
+ aItem = runtime.pyObject2Any( rItem.get() );
+ }
+ catch ( const css::uno::RuntimeException & )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw css::script::CannotConvertException();
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ if ( isTuple )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xIndexReplace->getElementType();
+ aItem = runtime.getImpl()->cargo->xTypeConverter->convertTo( aItem, aType );
+ }
+
+ if ( i < nSliceLength )
+ {
+ xIndexReplace->replaceByIndex( nCur, aItem );
+ }
+ else
+ {
+ xIndexContainer->insertByIndex( nCur, aItem );
+ }
+ }
+ }
+ else
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer->removeByIndex( nCur );
+ nCur--;
+ }
+ }
+
+ return 0;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+static int lcl_setitem_string( PyUNO const *me, PyObject *pKey, PyObject *pValue )
+{
+ Runtime runtime;
+
+ OUString sKey = pyString2ustring( pKey );
+ bool isTuple = false;
+
+ Any aValue;
+ if ( pValue != nullptr)
+ {
+ isTuple = PyTuple_Check( pValue );
+ try
+ {
+ aValue = runtime.pyObject2Any( pValue );
+ }
+ catch( const css::uno::RuntimeException & )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw css::script::CannotConvertException();
+ }
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XNameContainer > xNameContainer( me->members->xInvocation, UNO_QUERY );
+ Reference< XNameReplace > xNameReplace;
+ if ( xNameContainer.is() )
+ xNameReplace = xNameContainer;
+ else
+ xNameReplace.set( me->members->xInvocation, UNO_QUERY );
+
+ if ( xNameReplace.is() )
+ {
+ if ( isTuple && aValue.hasValue() )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xNameReplace->getElementType();
+ aValue = runtime.getImpl()->cargo->xTypeConverter->convertTo( aValue, aType );
+ }
+
+ if ( aValue.hasValue() )
+ {
+ if ( xNameContainer.is() )
+ {
+ try {
+ xNameContainer->insertByName( sKey, aValue );
+ return 0;
+ }
+ catch( const css::container::ElementExistException & )
+ {
+ // Fall through, try replace instead
+ }
+ }
+
+ xNameReplace->replaceByName( sKey, aValue );
+ return 0;
+ }
+ else if ( xNameContainer.is() )
+ {
+ xNameContainer->removeByName( sKey );
+ return 0;
+ }
+ }
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+static int PyUNO_setitem( PyObject *self, PyObject *pKey, PyObject *pValue )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ if ( PyIndex_Check( pKey ) )
+ {
+ return lcl_setitem_index( me, pKey, pValue );
+ }
+ else if ( PySlice_Check( pKey ) )
+ {
+ return lcl_setitem_slice( me, pKey, pValue );
+ }
+ else if ( PyUnicode_Check( pKey ) )
+ {
+ return lcl_setitem_string( me, pKey, pValue );
+ }
+
+ PyErr_SetString( PyExc_TypeError, "list index has invalid type" );
+ }
+ catch( const css::lang::IndexOutOfBoundsException & )
+ {
+ PyErr_SetString( PyExc_IndexError, "list index out of range" );
+ }
+ catch( const css::container::NoSuchElementException & )
+ {
+ PyErr_SetString( PyExc_KeyError, "key not found" );
+ }
+ catch( const css::lang::IllegalArgumentException & )
+ {
+ PyErr_SetString( PyExc_TypeError, "value has invalid type" );
+ }
+ catch( const css::script::CannotConvertException & )
+ {
+ PyErr_SetString( PyExc_TypeError, "value has invalid type" );
+ }
+ catch( const css::container::ElementExistException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return 1;
+}
+
+static PyObject* PyUNO_iter( PyObject *self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess;
+ Reference< XEnumeration > xEnumeration;
+ Reference< XIndexAccess > xIndexAccess;
+ Reference< XNameAccess > xNameAccess;
+
+ {
+ PyThreadDetach antiguard;
+
+ xEnumerationAccess.set( me->members->xInvocation, UNO_QUERY );
+ if ( xEnumerationAccess.is() )
+ xEnumeration = xEnumerationAccess->createEnumeration();
+ else
+ xEnumeration.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( !xEnumeration.is() )
+ xIndexAccess.set( me->members->xInvocation, UNO_QUERY );
+
+ if ( !xIndexAccess.is() )
+ xNameAccess.set( me->members->xInvocation, UNO_QUERY );
+ }
+
+ // XEnumerationAccess iterator
+ // XEnumeration iterator
+ if (xEnumeration.is())
+ {
+ return PyUNO_iterator_new( xEnumeration );
+ }
+
+ // XIndexAccess iterator
+ if ( xIndexAccess.is() )
+ {
+ // We'd like to be able to use PySeqIter_New() here, but we're not
+ // allowed to because we also implement the mapping protocol
+ return PyUNO_list_iterator_new( xIndexAccess );
+ }
+
+ // XNameAccess iterator
+ if (xNameAccess.is())
+ {
+ // There's no generic mapping iterator, but we can cobble our own
+ // together using PySeqIter_New()
+ Runtime runtime;
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+ aRet <<= xNameAccess->getElementNames();
+ }
+ PyRef rNames = runtime.any2PyObject( aRet );
+ return PySeqIter_New( rNames.getAcquired() );
+ }
+
+ PyErr_SetString ( PyExc_TypeError, "object is not iterable" );
+ }
+ catch( css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return nullptr;
+}
+
+static int PyUNO_contains( PyObject *self, PyObject *pKey )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ Runtime runtime;
+
+ try
+ {
+ Any aValue;
+ try
+ {
+ aValue = runtime.pyObject2Any( pKey );
+ }
+ catch( const css::uno::RuntimeException & )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw css::script::CannotConvertException();
+ }
+
+ // XNameAccess is tried first, because checking key presence is much more
+ // useful for objects which implement both XIndexAccess and XNameAccess
+
+ // For XNameAccess
+ if ( PyUnicode_Check( pKey ) )
+ {
+ OUString sKey;
+ aValue >>= sKey;
+ Reference< XNameAccess > xNameAccess;
+
+ {
+ PyThreadDetach antiguard;
+
+ xNameAccess.set( me->members->xInvocation, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ bool hasKey = xNameAccess->hasByName( sKey );
+ return hasKey ? 1 : 0;
+ }
+ }
+ }
+
+ // For any other type of PyUNO iterable: Ugly iterative search by
+ // content (XIndexAccess, XEnumerationAccess, XEnumeration)
+ PyRef rIterator( PyUNO_iter( self ), SAL_NO_ACQUIRE );
+ if ( rIterator.is() )
+ {
+ while ( PyObject* pItem = PyIter_Next( rIterator.get() ) )
+ {
+ PyRef rItem( pItem, SAL_NO_ACQUIRE );
+ if ( PyObject_RichCompareBool( pKey, rItem.get(), Py_EQ ) == 1 )
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "argument is not iterable" );
+ }
+ catch( const css::script::CannotConvertException& )
+ {
+ PyErr_SetString( PyExc_TypeError, "invalid type passed as left argument to 'in'" );
+ }
+ catch( const css::container::NoSuchElementException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::IndexOutOfBoundsException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return -1;
+}
+
+static PyObject* PyUNO_getattr (PyObject* self, char* name)
+{
+ try
+ {
+
+ Runtime runtime;
+
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+ if (strcmp (name, "__dict__") == 0)
+ {
+ Py_INCREF (Py_TYPE(me)->tp_dict);
+ return Py_TYPE(me)->tp_dict;
+ }
+ if (strcmp (name, "__class__") == 0)
+ {
+ Py_INCREF (Py_None);
+ return Py_None;
+ }
+
+ PyObject *pRet = PyObject_GenericGetAttr( self, PyUnicode_FromString( name ) );
+ if( pRet )
+ return pRet;
+ PyErr_Clear();
+
+ OUString attrName( OUString::createFromAscii( name ) );
+ //We need to find out if it's a method...
+ bool isMethod;
+ {
+ PyThreadDetach antiguard;
+ isMethod = me->members->xInvocation->hasMethod (attrName);
+ }
+ if (isMethod)
+ {
+ //Create a callable object to invoke this...
+ PyRef ret = PyUNO_callable_new (
+ me->members->xInvocation,
+ attrName);
+ Py_XINCREF( ret.get() );
+ return ret.get();
+
+ }
+
+ //or a property
+ bool isProperty;
+ Any anyRet;
+ {
+ PyThreadDetach antiguard;
+ isProperty = me->members->xInvocation->hasProperty ( attrName);
+ if (isProperty)
+ {
+ //Return the value of the property
+ anyRet = me->members->xInvocation->getValue (attrName);
+ }
+ }
+ if (isProperty)
+ {
+ PyRef ret = runtime.any2PyObject(anyRet);
+ Py_XINCREF( ret.get() );
+ return ret.get();
+ }
+
+ //or else...
+ PyErr_SetString (PyExc_AttributeError, name);
+ }
+ catch( const css::reflection::InvocationTargetException & e )
+ {
+ raisePyExceptionWithAny( e.TargetException );
+ }
+ catch( const css::beans::UnknownPropertyException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const RuntimeException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+
+ return nullptr;
+}
+
+static int PyUNO_setattr (PyObject* self, char* name, PyObject* value)
+{
+ PyUNO* me;
+
+ me = reinterpret_cast<PyUNO*>(self);
+ try
+ {
+ Runtime runtime;
+ Any val= runtime.pyObject2Any(value, ACCEPT_UNO_ANY);
+
+ OUString attrName( OUString::createFromAscii( name ) );
+ {
+ PyThreadDetach antiguard;
+ if (me->members->xInvocation->hasProperty (attrName))
+ {
+ me->members->xInvocation->setValue (attrName, val);
+ return 0; //Keep with Python's boolean system
+ }
+ }
+ }
+ catch( const css::reflection::InvocationTargetException & e )
+ {
+ raisePyExceptionWithAny( e.TargetException );
+ return 1;
+ }
+ catch( const css::beans::UnknownPropertyException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ return 1;
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ return 1;
+ }
+ catch( const RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ return 1;
+ }
+ PyErr_SetString (PyExc_AttributeError, name);
+ return 1; //as above.
+}
+
+static PyObject* PyUNO_cmp( PyObject *self, PyObject *that, int op )
+{
+ PyObject *result;
+
+ if(op != Py_EQ && op != Py_NE)
+ {
+ PyErr_SetString(PyExc_TypeError, "only '==' and '!=' comparisons are defined");
+ return nullptr;
+ }
+ if( self == that )
+ {
+ result = (op == Py_EQ ? Py_True : Py_False);
+ Py_INCREF(result);
+ return result;
+ }
+ try
+ {
+ Runtime runtime;
+ if( PyObject_IsInstance( that, getPyUnoClass().get() ) )
+ {
+
+ PyUNO *me = reinterpret_cast< PyUNO*> ( self );
+ PyUNO *other = reinterpret_cast< PyUNO *> (that );
+ css::uno::TypeClass tcMe = me->members->wrappedObject.getValueTypeClass();
+ css::uno::TypeClass tcOther = other->members->wrappedObject.getValueTypeClass();
+
+ if( tcMe == tcOther )
+ {
+ if( me->members->wrappedObject == other->members->wrappedObject )
+ {
+ result = (op == Py_EQ ? Py_True : Py_False);
+ Py_INCREF(result);
+ return result;
+ }
+ }
+ }
+ }
+ catch( const css::uno::RuntimeException & e)
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+
+ result = (op == Py_EQ ? Py_False : Py_True);
+ Py_INCREF(result);
+ return result;
+}
+
+static PyMethodDef PyUNOMethods[] =
+{
+ {"__dir__", reinterpret_cast<PyCFunction>(PyUNO_dir), METH_NOARGS, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+static PyNumberMethods PyUNONumberMethods[] =
+{
+ nullptr, /* nb_add */
+ nullptr, /* nb_subtract */
+ nullptr, /* nb_multiply */
+ nullptr, /* nb_remainder */
+ nullptr, /* nb_divmod */
+ nullptr, /* nb_power */
+ nullptr, /* nb_negative */
+ nullptr, /* nb_positive */
+ nullptr, /* nb_absolute */
+ PyUNO_bool, /* nb_bool */
+ nullptr, /* nb_invert */
+ nullptr, /* nb_lshift */
+ nullptr, /* nb_rshift */
+ nullptr, /* nb_and */
+ nullptr, /* nb_xor */
+ nullptr, /* nb_or */
+ nullptr, /* nb_int */
+ nullptr, /* nb_reserved */
+ nullptr, /* nb_float */
+ nullptr, /* nb_inplace_add */
+ nullptr, /* nb_inplace_subtract */
+ nullptr, /* nb_inplace_multiply */
+ nullptr, /* nb_inplace_remainder */
+ nullptr, /* nb_inplace_power */
+ nullptr, /* nb_inplace_lshift */
+ nullptr, /* nb_inplace_rshift */
+ nullptr, /* nb_inplace_and */
+ nullptr, /* nb_inplace_xor */
+ nullptr, /* nb_inplace_or */
+
+ nullptr, /* nb_floor_divide */
+ nullptr, /* nb_true_divide */
+ nullptr, /* nb_inplace_floor_divide */
+ nullptr, /* nb_inplace_true_divide */
+
+ nullptr, /* nb_index */
+#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5
+ nullptr, /* nb_matrix_multiply */
+ nullptr, /* nb_inplace_matrix_multiply */
+#endif
+};
+
+static PySequenceMethods PyUNOSequenceMethods[] =
+{
+ nullptr, /* sq_length */
+ nullptr, /* sq_concat */
+ nullptr, /* sq_repeat */
+ nullptr, /* sq_item */
+ nullptr, /* sq_slice */
+ nullptr, /* sq_ass_item */
+ nullptr, /* sq_ass_slice */
+ PyUNO_contains, /* sq_contains */
+ nullptr, /* sq_inplace_concat */
+ nullptr /* sq_inplace_repeat */
+};
+
+static PyMappingMethods PyUNOMappingMethods[] =
+{
+ PyUNO_len, /* mp_length */
+ PyUNO_getitem, /* mp_subscript */
+ PyUNO_setitem, /* mp_ass_subscript */
+};
+
+static PyTypeObject PyUNOType =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "pyuno",
+ sizeof (PyUNO),
+ 0,
+ PyUNO_del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ PyUNO_getattr,
+ PyUNO_setattr,
+ /* this type does not exist in Python 3: (cmpfunc) */ nullptr,
+ PyUNO_repr,
+ PyUNONumberMethods,
+ PyUNOSequenceMethods,
+ PyUNOMappingMethods,
+ PyUNO_hash,
+ nullptr,
+ PyUNO_str,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_ITER | Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_HAVE_SEQUENCE_IN,
+ nullptr,
+ nullptr,
+ nullptr,
+ PyUNO_cmp,
+ 0,
+ PyUNO_iter,
+ nullptr,
+ PyUNOMethods,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+ , 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+int PyUNO_initType()
+{
+ return PyType_Ready(&PyUNOType);
+}
+
+PyRef getPyUnoClass()
+{
+ return PyRef( reinterpret_cast< PyObject * > ( &PyUNOType ) );
+}
+
+PyRef PyUNO_new (
+ const Any &targetInterface,
+ const Reference<XSingleServiceFactory> &ssf )
+{
+ Reference<XInvocation2> xInvocation;
+
+ {
+ PyThreadDetach antiguard;
+ xInvocation.set(
+ ssf->createInstanceWithArguments( Sequence<Any>( &targetInterface, 1 ) ), css::uno::UNO_QUERY_THROW );
+
+ auto that = comphelper::getFromUnoTunnel<Adapter>(
+ xInvocation->getIntrospection()->queryAdapter(cppu::UnoType<XUnoTunnel>::get()));
+ if( that )
+ return that->getWrappedObject();
+ }
+ if( !Py_IsInitialized() )
+ throw RuntimeException();
+
+ PyUNO* self = PyObject_New (PyUNO, &PyUNOType);
+ if (self == nullptr)
+ return PyRef(); // == error
+ self->members = new PyUNOInternals;
+ self->members->xInvocation = xInvocation;
+ self->members->wrappedObject = targetInterface;
+ return PyRef( reinterpret_cast<PyObject*>(self), SAL_NO_ACQUIRE );
+
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_adapter.cxx b/pyuno/source/module/pyuno_adapter.cxx
new file mode 100644
index 0000000000..595e09a1f8
--- /dev/null
+++ b/pyuno/source/module/pyuno_adapter.cxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "pyuno_impl.hxx"
+
+#include <o3tl/any.hxx>
+
+#include <com/sun/star/beans/MethodConcept.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XInvocationAdapterFactory2.hpp>
+#include <com/sun/star/beans/XIntrospection.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <utility>
+
+
+using com::sun::star::beans::XIntrospectionAccess;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Type;
+using com::sun::star::lang::XUnoTunnel;
+using com::sun::star::lang::IllegalArgumentException;
+using com::sun::star::beans::UnknownPropertyException;
+using com::sun::star::script::CannotConvertException;
+using com::sun::star::reflection::InvocationTargetException;
+using com::sun::star::reflection::XIdlMethod;
+using com::sun::star::reflection::ParamInfo;
+
+#define TO_ASCII(x) OUStringToOString( x , RTL_TEXTENCODING_ASCII_US).getStr()
+
+namespace pyuno
+{
+
+Adapter::Adapter( PyRef ref, const Sequence< Type > &types )
+ : mWrappedObject(std::move( ref )),
+ mInterpreter( (PyThreadState_Get()->interp) ),
+ mTypes( types )
+{}
+
+Adapter::~Adapter()
+{
+ // Problem: We don't know, if we have the python interpreter lock
+ // There is no runtime function to get to know this.
+ decreaseRefCount( mInterpreter, mWrappedObject.get() );
+ mWrappedObject.scratch();
+}
+
+const Sequence<sal_Int8> & Adapter::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit g_id;
+ return g_id.getSeq();
+}
+
+sal_Int64 Adapter::getSomething( const Sequence< sal_Int8 > &id)
+{
+ return comphelper::getSomethingImpl(id, this);
+}
+
+void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime )
+{
+ if( !Py_IsInitialized() )
+ throw InvocationTargetException();
+
+ if( PyErr_Occurred() )
+ {
+ PyRef excType, excValue, excTraceback;
+ PyErr_Fetch(reinterpret_cast<PyObject **>(&excType), reinterpret_cast<PyObject**>(&excValue), reinterpret_cast<PyObject**>(&excTraceback));
+ Any unoExc( runtime.extractUnoException( excType, excValue, excTraceback ) );
+ throw InvocationTargetException(
+ o3tl::doAccess<css::uno::Exception>(unoExc)->Message,
+ Reference<XInterface>(), unoExc );
+ }
+}
+
+Reference< XIntrospectionAccess > Adapter::getIntrospection()
+{
+ // not supported
+ return Reference< XIntrospectionAccess > ();
+}
+
+Sequence< sal_Int16 > Adapter::getOutIndexes( const OUString & functionName )
+{
+ Sequence< sal_Int16 > ret;
+ MethodOutIndexMap::const_iterator ii = m_methodOutIndexMap.find( functionName );
+ if( ii == m_methodOutIndexMap.end() )
+ {
+
+ Runtime runtime;
+ {
+ PyThreadDetach antiguard;
+
+ // retrieve the adapter object again. It will be the same instance as before,
+ // (the adapter factory keeps a weak map inside, which I couldn't have outside)
+ Reference< XInterface > unoAdapterObject =
+ runtime.getImpl()->cargo->xAdapterFactory->createAdapter( this, mTypes );
+
+ // uuuh, that's really expensive. The alternative would have been, to store
+ // an instance of the introspection at (this), but this results in a cyclic
+ // reference, which is never broken (as it is up to OOo1.1.0).
+ Reference< XIntrospectionAccess > introspection =
+ runtime.getImpl()->cargo->xIntrospection->inspect( Any( unoAdapterObject ) );
+
+ if( !introspection.is() )
+ {
+ throw RuntimeException(
+ "pyuno bridge: Couldn't inspect uno adapter ( the python class must implement com.sun.star.lang.XTypeProvider !)" );
+ }
+
+ Reference< XIdlMethod > method = introspection->getMethod(
+ functionName, css::beans::MethodConcept::ALL );
+ if( ! method.is( ) )
+ {
+ throw RuntimeException(
+ "pyuno bridge: Couldn't get reflection for method " + functionName );
+ }
+
+ const Sequence< ParamInfo > seqInfo = method->getParameterInfos();
+ std::vector<sal_Int16> retVec;
+ for( sal_Int32 i = 0; i < seqInfo.getLength(); ++i )
+ {
+ if( seqInfo[i].aMode == css::reflection::ParamMode_OUT ||
+ seqInfo[i].aMode == css::reflection::ParamMode_INOUT )
+ {
+ retVec.push_back(static_cast<sal_Int16>(i));
+ }
+ }
+
+ ret = comphelper::containerToSequence(retVec);
+ }
+ // guard active again !
+ m_methodOutIndexMap[ functionName ] = ret;
+ }
+ else
+ {
+ ret = ii->second;
+ }
+ return ret;
+}
+
+Any Adapter::invoke( const OUString &aFunctionName,
+ const Sequence< Any >& aParams,
+ Sequence< sal_Int16 > &aOutParamIndex,
+ Sequence< Any > &aOutParam)
+{
+ Any ret;
+
+ // special hack for the uno object identity concept. The XUnoTunnel.getSomething() call is
+ // always handled by the adapter directly.
+ if( aParams.getLength() == 1 && aFunctionName == "getSomething" )
+ {
+ Sequence< sal_Int8 > id;
+ if( aParams[0] >>= id )
+ return css::uno::Any( getSomething( id ) );
+
+ }
+
+ RuntimeCargo *cargo = nullptr;
+ try
+ {
+ PyThreadAttach guard( mInterpreter );
+ {
+ if( !Py_IsInitialized() )
+ throw InvocationTargetException();
+
+ // convert parameters to python args
+ // TODO: Out parameter
+ Runtime runtime;
+ cargo = runtime.getImpl()->cargo;
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logCall( cargo, "try uno->py[0x",
+ mWrappedObject.get(), aFunctionName, aParams );
+ }
+
+ sal_Int32 size = aParams.getLength();
+ PyRef argsTuple(PyTuple_New( size ), SAL_NO_ACQUIRE, NOT_NULL );
+ int i;
+ // fill tuple with default values in case of exceptions
+ for( i = 0 ;i < size ; i ++ )
+ {
+ Py_INCREF( Py_None );
+ PyTuple_SetItem( argsTuple.get(), i, Py_None );
+ }
+
+ // convert args to python
+ for( i = 0; i < size ; i ++ )
+ {
+ PyRef val = runtime.any2PyObject( aParams[i] );
+
+ // any2PyObject() can release the GIL
+ if( !Py_IsInitialized() )
+ throw InvocationTargetException();
+
+ PyTuple_SetItem( argsTuple.get(), i, val.getAcquired() );
+ }
+
+ // get callable
+ PyRef method(PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aFunctionName)),
+ SAL_NO_ACQUIRE);
+
+ raiseInvocationTargetExceptionWhenNeeded( runtime);
+ if( !method.is() )
+ {
+ PyRef str( PyObject_Repr( mWrappedObject.get() ), SAL_NO_ACQUIRE );
+
+ OUString sMsg = "pyuno::Adapter: Method "
+ + aFunctionName
+ + " is not implemented at object "
+ + pyString2ustring(str.get());
+ throw IllegalArgumentException( sMsg, Reference< XInterface > (),0 );
+ }
+
+ PyRef pyRet( PyObject_CallObject( method.get(), argsTuple.get() ), SAL_NO_ACQUIRE );
+ raiseInvocationTargetExceptionWhenNeeded( runtime);
+ if( pyRet.is() )
+ {
+ ret = runtime.pyObject2Any( pyRet );
+
+ if( ret.hasValue() &&
+ ret.getValueTypeClass() == css::uno::TypeClass_SEQUENCE &&
+ aFunctionName != "getTypes" && // needed by introspection itself !
+ aFunctionName != "getImplementationId" ) // needed by introspection itself !
+ {
+ // the sequence can either be
+ // 1) a simple sequence return value
+ // 2) a sequence, where the first element is the return value
+ // and the following elements are interpreted as the outparameter
+ // I can only decide for one solution by checking the method signature,
+ // so I need the reflection of the adapter !
+ aOutParamIndex = getOutIndexes( aFunctionName );
+ if( aOutParamIndex.hasElements() )
+ {
+ // out parameters exist, extract the sequence
+ Sequence< Any > seq;
+ if( ! ( ret >>= seq ) )
+ {
+ throw RuntimeException(
+ "pyuno bridge: Couldn't extract out parameters for method " + aFunctionName );
+ }
+
+ auto nOutLength = aOutParamIndex.getLength();
+ if( nOutLength + 1 != seq.getLength() )
+ {
+ OUString sMsg = "pyuno bridge: expected for method "
+ + aFunctionName
+ + " one return value and "
+ + OUString::number(nOutLength)
+ + " out parameters, got a sequence of "
+ + OUString::number(seq.getLength())
+ + " elements as return value.";
+ throw RuntimeException( sMsg, *this );
+ }
+
+ aOutParam.realloc( nOutLength );
+ ret = std::as_const(seq)[0];
+ std::copy_n(std::next(std::cbegin(seq)), nOutLength, aOutParam.getArray());
+ }
+ // else { sequence is a return value !}
+ }
+ }
+
+ // log the reply, if desired
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logReply( cargo, "success uno->py[0x" ,
+ mWrappedObject.get(), aFunctionName, ret, aOutParam );
+ }
+ }
+
+ }
+ catch( const InvocationTargetException & e )
+ {
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException(
+ cargo, "except uno->py[0x" ,
+ mWrappedObject.get(), aFunctionName,
+ e.TargetException.getValue(),e.TargetException.getValueType() );
+ }
+ throw;
+ }
+ catch( const IllegalArgumentException & e )
+ {
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException(
+ cargo, "except uno->py[0x" ,
+ mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
+ }
+ throw;
+ }
+ catch( const RuntimeException & e )
+ {
+ if( cargo && isLog( cargo, LogLevel::CALL ) )
+ {
+ logException(
+ cargo, "except uno->py[0x" ,
+ mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
+ }
+ throw;
+ }
+ catch( const CannotConvertException & e )
+ {
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException(
+ cargo, "except uno->py[0x" ,
+ mWrappedObject.get(), aFunctionName, &e,cppu::UnoType<decltype(e)>::get() );
+ }
+ throw;
+ }
+ return ret;
+}
+
+void Adapter::setValue( const OUString & aPropertyName, const Any & value )
+{
+ if( !hasProperty( aPropertyName ) )
+ {
+ throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName + " is unknown." );
+ }
+
+ PyThreadAttach guard( mInterpreter );
+ try
+ {
+ if( !Py_IsInitialized() )
+ throw InvocationTargetException();
+
+ Runtime runtime;
+ PyRef obj = runtime.any2PyObject( value );
+
+ // any2PyObject() can release the GIL
+ if( !Py_IsInitialized() )
+ throw InvocationTargetException();
+
+ PyObject_SetAttrString(
+ mWrappedObject.get(), TO_ASCII(aPropertyName), obj.get() );
+ raiseInvocationTargetExceptionWhenNeeded( runtime);
+
+ }
+ catch( const IllegalArgumentException & exc )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw InvocationTargetException( exc.Message, *this, anyEx );
+ }
+}
+
+Any Adapter::getValue( const OUString & aPropertyName )
+{
+ Any ret;
+ PyThreadAttach guard( mInterpreter );
+ {
+ // Should probably be InvocationTargetException, but the interface doesn't allow it
+ if( !Py_IsInitialized() )
+ throw RuntimeException();
+
+ Runtime runtime;
+ PyRef pyRef(
+ PyObject_GetAttrString( mWrappedObject.get(), TO_ASCII(aPropertyName) ),
+ SAL_NO_ACQUIRE );
+
+ if (!pyRef.is() || PyErr_Occurred())
+ {
+ throw UnknownPropertyException( "pyuno::Adapter: Property " + aPropertyName + " is unknown." );
+ }
+ ret = runtime.pyObject2Any( pyRef );
+ }
+ return ret;
+}
+
+sal_Bool Adapter::hasMethod( const OUString & aMethodName )
+{
+ return hasProperty( aMethodName );
+}
+
+sal_Bool Adapter::hasProperty( const OUString & aPropertyName )
+{
+ bool bRet = false;
+ PyThreadAttach guard( mInterpreter );
+ {
+ // Should probably be InvocationTargetException, but the interface doesn't allow it
+ if( !Py_IsInitialized() )
+ throw RuntimeException();
+
+ bRet = PyObject_HasAttrString(
+ mWrappedObject.get() , TO_ASCII( aPropertyName ));
+ }
+ return bRet;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_callable.cxx b/pyuno/source/module/pyuno_callable.cxx
new file mode 100644
index 0000000000..f22de316b7
--- /dev/null
+++ b/pyuno/source/module/pyuno_callable.cxx
@@ -0,0 +1,274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "pyuno_impl.hxx"
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::script::XInvocation2;
+
+namespace pyuno
+{
+namespace {
+
+struct PyUNO_callable_Internals
+{
+ Reference<XInvocation2> xInvocation;
+ OUString methodName;
+ ConversionMode mode;
+};
+
+struct PyUNO_callable
+{
+ PyObject_HEAD
+ PyUNO_callable_Internals* members;
+};
+
+}
+
+static void PyUNO_callable_del (PyObject* self)
+{
+ PyUNO_callable* me;
+
+ me = reinterpret_cast<PyUNO_callable*>(self);
+ delete me->members;
+ PyObject_Del (self);
+}
+
+static PyObject* PyUNO_callable_call(
+ PyObject* self, PyObject* args, SAL_UNUSED_PARAMETER PyObject*)
+{
+ PyUNO_callable* me;
+
+ Sequence<short> aOutParamIndex;
+ Sequence<Any> aOutParam;
+ Sequence<Any> aParams;
+ Any any_params;
+ Any ret_value;
+ RuntimeCargo *cargo = nullptr;
+ me = reinterpret_cast<PyUNO_callable*>(self);
+
+ PyRef ret;
+ try
+ {
+ Runtime runtime;
+ cargo = runtime.getImpl()->cargo;
+ any_params = runtime.pyObject2Any (args, me->members->mode);
+
+ if (any_params.getValueTypeClass () == css::uno::TypeClass_SEQUENCE)
+ {
+ any_params >>= aParams;
+ }
+ else
+ {
+ aParams = { any_params };
+ }
+
+ {
+ PyThreadDetach antiguard; //python free zone
+
+ // do some logging if desired ...
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logCall( cargo, "try py->uno[0x", me->members->xInvocation.get(),
+ me->members->methodName, aParams );
+ }
+
+ // do the call
+ ret_value = me->members->xInvocation->invoke (
+ me->members->methodName, aParams, aOutParamIndex, aOutParam);
+
+ // log the reply, if desired
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logReply( cargo, "success py->uno[0x", me->members->xInvocation.get(),
+ me->members->methodName, ret_value, aOutParam);
+ }
+ }
+
+
+ PyRef temp = runtime.any2PyObject (ret_value);
+ if( aOutParam.getLength() )
+ {
+ PyRef return_list( PyTuple_New (1+aOutParam.getLength()), SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem (return_list.get(), 0, temp.getAcquired());
+
+ // initialize with defaults in case of exceptions
+ int i;
+ for( i = 1 ; i < 1+aOutParam.getLength() ; i ++ )
+ {
+ Py_INCREF( Py_None );
+ PyTuple_SetItem( return_list.get() , i , Py_None );
+ }
+
+ for( i = 0 ; i < aOutParam.getLength() ; i ++ )
+ {
+ PyRef ref = runtime.any2PyObject( aOutParam[i] );
+ PyTuple_SetItem (return_list.get(), 1+i, ref.getAcquired());
+ }
+ ret = return_list;
+ }
+ else
+ {
+ ret = temp;
+ }
+ }
+ catch( const css::reflection::InvocationTargetException & e )
+ {
+
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException( cargo, "except py->uno[0x", me->members->xInvocation.get() ,
+ me->members->methodName, e.TargetException.getValue(), e.TargetException.getValueTypeRef());
+ }
+ raisePyExceptionWithAny( e.TargetException );
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException( cargo, "error py->uno[0x", me->members->xInvocation.get() ,
+ me->members->methodName, &e, cppu::UnoType<decltype(e)>::get().getTypeLibType());
+ }
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::IllegalArgumentException &e )
+ {
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ logException( cargo, "error py->uno[0x", me->members->xInvocation.get() ,
+ me->members->methodName, &e, cppu::UnoType<decltype(e)>::get().getTypeLibType());
+ }
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch (const css::uno::RuntimeException &e)
+ {
+ if( cargo && isLog( cargo, LogLevel::CALL ) )
+ {
+ logException( cargo, "error py->uno[0x", me->members->xInvocation.get() ,
+ me->members->methodName, &e, cppu::UnoType<decltype(e)>::get().getTypeLibType());
+ }
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return ret.getAcquired();
+}
+
+
+static PyTypeObject PyUNO_callable_Type =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "PyUNO_callable",
+ sizeof (PyUNO_callable),
+ 0,
+ ::pyuno::PyUNO_callable_del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ ::pyuno::PyUNO_callable_call,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+ , 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+PyRef PyUNO_callable_new (
+ const Reference<XInvocation2> &my_inv,
+ const OUString & methodName,
+ enum ConversionMode mode )
+{
+ PyUNO_callable* self;
+
+ OSL_ENSURE (my_inv.is(), "XInvocation must be valid");
+
+ self = PyObject_New (PyUNO_callable, &PyUNO_callable_Type);
+ if (self == nullptr)
+ return nullptr; //NULL == Error!
+
+ self->members = new PyUNO_callable_Internals;
+ self->members->xInvocation = my_inv;
+ self->members->methodName = methodName;
+ self->members->mode = mode;
+
+ return PyRef( reinterpret_cast<PyObject*>(self), SAL_NO_ACQUIRE );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_dlopenwrapper.c b/pyuno/source/module/pyuno_dlopenwrapper.c
new file mode 100644
index 0000000000..78e6982e89
--- /dev/null
+++ b/pyuno/source/module/pyuno_dlopenwrapper.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/* make Python.h go first as a hack to work around _POSIX_C_SOURCE redefinition
+ warnings: */
+#include <Python.h>
+
+#include <sal/config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#if defined LINUX && !defined __USE_GNU
+#define __USE_GNU
+#endif
+#include <dlfcn.h>
+
+#include <rtl/string.h>
+
+/* A wrapper around libpyuno.so, making sure the latter is loaded RTLD_GLOBAL
+ so that C++ exception handling works with old GCC versions (that determine
+ RTTI identity by comparing string addresses rather than string content).
+*/
+
+static void * load(void const * address, char const * symbol) {
+ Dl_info dl_info;
+ char * slash;
+ size_t len;
+ char * libname;
+ void * h;
+ void * func;
+ if (dladdr(address, &dl_info) == 0) {
+ abort();
+ }
+ slash = strrchr(dl_info.dli_fname, '/');
+ if (slash == NULL) {
+ abort();
+ }
+ len = slash - dl_info.dli_fname + 1;
+ libname = malloc(
+ len + RTL_CONSTASCII_LENGTH(SAL_DLLPREFIX "pyuno" SAL_DLLEXTENSION)
+ + 1);
+ if (libname == NULL) {
+ abort();
+ }
+ strncpy(libname, dl_info.dli_fname, len);
+ strcpy(libname + len, SAL_DLLPREFIX "pyuno" SAL_DLLEXTENSION);
+ h = dlopen(libname, RTLD_LAZY | RTLD_GLOBAL);
+ free(libname);
+ if (h == NULL) {
+ fprintf(stderr, "failed to load pyuno: '%s'\n", dlerror());
+ abort();
+ }
+ func = dlsym(h, symbol);
+ if (func == NULL) {
+ dlclose(h);
+ abort();
+ }
+ // coverity[leaked_storage] - this is on purpose
+ return func;
+}
+
+SAL_DLLPUBLIC_EXPORT PyObject * PyInit_pyuno(void) {
+ return
+ ((PyObject * (*)(void)) load((void *) &PyInit_pyuno, "PyInit_pyuno"))();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_except.cxx b/pyuno/source/module/pyuno_except.cxx
new file mode 100644
index 0000000000..b833970e87
--- /dev/null
+++ b/pyuno/source/module/pyuno_except.cxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "pyuno_impl.hxx"
+
+#include <typelib/typedescription.hxx>
+#include <com/sun/star/script/CannotConvertException.hpp>
+
+
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::TypeDescription;
+
+namespace pyuno
+{
+
+void raisePyExceptionWithAny( const css::uno::Any &anyExc )
+{
+ try
+ {
+ Runtime runtime;
+ PyRef exc = runtime.any2PyObject( anyExc );
+ if( exc.is() )
+ {
+ PyRef type( getClass( anyExc.getValueType().getTypeName(),runtime ) );
+ PyErr_SetObject( type.get(), exc.get());
+ }
+ else
+ {
+ css::uno::Exception e;
+ anyExc >>= e;
+
+ OUString buf = "Couldn't convert uno exception to a python exception (" +
+ anyExc.getValueType().getTypeName() + ": " + e.Message + ")";
+ PyErr_SetString(
+ PyExc_SystemError,
+ OUStringToOString(buf,RTL_TEXTENCODING_ASCII_US).getStr() );
+ }
+ }
+ catch(const css::lang::IllegalArgumentException & e)
+ {
+ PyErr_SetString( PyExc_SystemError,
+ OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US).getStr() );
+ }
+ catch(const css::script::CannotConvertException & e)
+ {
+ PyErr_SetString( PyExc_SystemError,
+ OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US).getStr() );
+ }
+ catch(const RuntimeException & e)
+ {
+ PyErr_SetString( PyExc_SystemError,
+ OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US).getStr() );
+ }
+}
+
+/// @throws RuntimeException
+static PyRef createClass( const OUString & name, const Runtime &runtime )
+{
+ // assuming that this is never deleted !
+ // note I don't have the knowledge how to initialize these type objects correctly !
+ TypeDescription desc( name );
+ if( ! desc.is() )
+ {
+ throw RuntimeException( "pyuno.getClass: uno exception " + name + " is unknown" );
+ }
+
+ bool isStruct = desc.get()->eTypeClass == typelib_TypeClass_STRUCT;
+ bool isExc = desc.get()->eTypeClass == typelib_TypeClass_EXCEPTION;
+ bool isInterface = desc.get()->eTypeClass == typelib_TypeClass_INTERFACE;
+ if( !isStruct && !isExc && ! isInterface )
+ {
+ throw RuntimeException( "pyuno.getClass: " + name + "is a " +
+ OUString::createFromAscii( typeClassToString( static_cast<css::uno::TypeClass>(desc.get()->eTypeClass)) ) +
+ ", expected EXCEPTION, STRUCT or INTERFACE" );
+ }
+
+ // retrieve base class
+ PyRef base;
+ if( isInterface )
+ {
+ typelib_InterfaceTypeDescription *pDesc = reinterpret_cast<typelib_InterfaceTypeDescription *>(desc.get());
+ if( pDesc->pBaseTypeDescription )
+ {
+ base = getClass( pDesc->pBaseTypeDescription->aBase.pTypeName, runtime );
+ }
+ else
+ {
+ // must be XInterface !
+ }
+ }
+ else
+ {
+ typelib_CompoundTypeDescription *pDesc = reinterpret_cast<typelib_CompoundTypeDescription*>(desc.get());
+ if( pDesc->pBaseTypeDescription )
+ {
+ base = getClass( pDesc->pBaseTypeDescription->aBase.pTypeName, runtime );
+ }
+ else
+ {
+ if( isExc )
+ // we are currently creating the root UNO exception
+ base = PyRef(PyExc_Exception);
+ }
+ }
+ PyRef args( PyTuple_New( 3 ), SAL_NO_ACQUIRE, NOT_NULL );
+
+ PyRef pyTypeName = ustring2PyString( name /*.replace( '.', '_' )*/ );
+
+ PyRef bases;
+ if( base.is() )
+ {
+ { // for CC, keeping ref-count being 1
+ bases = PyRef( PyTuple_New( 1 ), SAL_NO_ACQUIRE );
+ }
+ PyTuple_SetItem( bases.get(), 0 , base.getAcquired() );
+ }
+ else
+ {
+ bases = PyRef( PyTuple_New( 0 ), SAL_NO_ACQUIRE );
+ }
+
+ PyTuple_SetItem( args.get(), 0, pyTypeName.getAcquired());
+ PyTuple_SetItem( args.get(), 1, bases.getAcquired() );
+ PyTuple_SetItem( args.get(), 2, PyDict_New() );
+
+ PyRef ret(
+ PyObject_CallObject(reinterpret_cast<PyObject *>(&PyType_Type) , args.get()),
+ SAL_NO_ACQUIRE );
+
+ // now overwrite ctor and attrib functions
+ if( isInterface )
+ {
+ PyObject_SetAttrString(
+ ret.get(), "__pyunointerface__",
+ ustring2PyString(name).get() );
+ }
+ else
+ {
+ PyRef ctor = getObjectFromUnoModule( runtime,"_uno_struct__init__" );
+ PyRef setter = getObjectFromUnoModule( runtime,"_uno_struct__setattr__" );
+ PyRef getter = getObjectFromUnoModule( runtime,"_uno_struct__getattr__" );
+ PyRef repr = getObjectFromUnoModule( runtime,"_uno_struct__repr__" );
+ PyRef eq = getObjectFromUnoModule( runtime,"_uno_struct__eq__" );
+ PyRef ne = getObjectFromUnoModule( runtime,"_uno_struct__ne__" );
+
+ PyObject_SetAttrString(
+ ret.get(), "__pyunostruct__",
+ ustring2PyString(name).get() );
+ PyObject_SetAttrString(
+ ret.get(), "typeName",
+ ustring2PyString(name).get() );
+ PyObject_SetAttrString(
+ ret.get(), "__init__", ctor.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__getattr__", getter.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__setattr__", setter.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__repr__", repr.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__str__", repr.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__eq__", eq.get() );
+ PyObject_SetAttrString(
+ ret.get(), "__ne__", ne.get() );
+ }
+ return ret;
+}
+
+bool isInstanceOfStructOrException( PyObject *obj)
+{
+ PyRef attr(
+ PyObject_GetAttrString(obj, "__class__"),
+ SAL_NO_ACQUIRE );
+ if(attr.is())
+ return PyObject_HasAttrString(attr.get(), "__pyunostruct__");
+ else
+ return false;
+}
+
+bool isInterfaceClass( const Runtime &runtime, PyObject * obj )
+{
+ const ClassSet & set = runtime.getImpl()->cargo->interfaceSet;
+ return set.find( obj ) != set.end();
+}
+
+PyRef getClass( const OUString & name , const Runtime &runtime)
+{
+ PyRef ret;
+
+ RuntimeCargo *cargo =runtime.getImpl()->cargo;
+ ExceptionClassMap::iterator ii = cargo->exceptionMap.find( name );
+ if( ii == cargo->exceptionMap.end() )
+ {
+ ret = createClass( name, runtime );
+ cargo->exceptionMap[name] = ret;
+ if( PyObject_HasAttrString(
+ ret.get(), "__pyunointerface__" ) )
+ cargo->interfaceSet.insert( ret );
+
+ PyObject_SetAttrString(
+ ret.get(), "__pyunointerface__",
+ ustring2PyString(name).get() );
+ }
+ else
+ {
+ ret = ii->second;
+ }
+
+ return ret;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_gc.cxx b/pyuno/source/module/pyuno_gc.cxx
new file mode 100644
index 0000000000..1efca400d5
--- /dev/null
+++ b/pyuno/source/module/pyuno_gc.cxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "pyuno_impl.hxx"
+
+#include <sal/config.h>
+
+#include <rtl/ref.hxx>
+#include <salhelper/thread.hxx>
+
+namespace pyuno
+{
+
+static bool g_destructorsOfStaticObjectsHaveBeenCalled;
+
+namespace {
+
+class StaticDestructorGuard
+{
+public:
+ ~StaticDestructorGuard()
+ {
+ g_destructorsOfStaticObjectsHaveBeenCalled = true;
+ }
+};
+
+}
+
+static StaticDestructorGuard guard;
+
+static bool isAfterUnloadOrPy_Finalize()
+{
+ return g_destructorsOfStaticObjectsHaveBeenCalled ||
+ !Py_IsInitialized();
+}
+
+namespace {
+
+class GCThread: public salhelper::Thread {
+public:
+ GCThread( PyInterpreterState *interpreter, PyObject * object );
+
+private:
+ virtual ~GCThread() override {}
+
+ virtual void execute() override;
+
+ PyObject *mPyObject;
+ PyInterpreterState *mPyInterpreter;
+};
+
+}
+
+GCThread::GCThread( PyInterpreterState *interpreter, PyObject * object ) :
+ Thread( "pyunoGCThread" ), mPyObject( object ),
+ mPyInterpreter( interpreter )
+{}
+
+void GCThread::execute()
+{
+ // otherwise we crash here, when main has been left already
+ if( isAfterUnloadOrPy_Finalize() )
+ return;
+ try
+ {
+ PyThreadAttach g( mPyInterpreter );
+ {
+ Runtime runtime;
+
+ // remove the reference from the pythonobject2adapter map
+ PyRef2Adapter::iterator ii =
+ runtime.getImpl()->cargo->mappedObjects.find( mPyObject );
+ if( ii != runtime.getImpl()->cargo->mappedObjects.end() )
+ {
+ runtime.getImpl()->cargo->mappedObjects.erase( ii );
+ }
+
+ Py_XDECREF( mPyObject );
+ }
+ }
+ catch( const css::uno::RuntimeException & e )
+ {
+ OString msg = OUStringToOString( e.Message, RTL_TEXTENCODING_ASCII_US );
+ fprintf( stderr, "Leaking python objects bridged to UNO for reason %s\n",msg.getStr());
+ }
+}
+
+void decreaseRefCount( PyInterpreterState *interpreter, PyObject *object )
+{
+ // otherwise we crash in the last after main ...
+ if( isAfterUnloadOrPy_Finalize() )
+ return;
+
+ // delegate to a new thread, because there does not seem
+ // to be a method, which tells, whether the global
+ // interpreter lock is held or not
+ // TODO: Look for a more efficient solution
+ try
+ {
+ rtl::Reference< GCThread >(new GCThread(interpreter, object))->launch();
+ //TODO: a protocol is missing how to join with the launched thread
+ // before exit(3), to ensure the thread is no longer relying on any
+ // infrastructure while that infrastructure is being shut down in
+ // atexit handlers
+ }
+ catch (std::runtime_error&)
+ {
+ // tdf#146621: Thread creation will fail on Windows with ERROR_ACCESS_DENIED
+ // when called at ExitProcess time; unhandled exception would hang the process
+ abort();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_impl.hxx b/pyuno/source/module/pyuno_impl.hxx
new file mode 100644
index 0000000000..946991c386
--- /dev/null
+++ b/pyuno/source/module/pyuno_impl.hxx
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#if defined(_MSC_VER)
+// Workaround for some horrible hypot() mess
+#include <math.h>
+#endif
+
+#include <Python.h>
+
+//Python 3.0 and newer don't have these flags
+#ifndef Py_TPFLAGS_HAVE_ITER
+# define Py_TPFLAGS_HAVE_ITER 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_RICHCOMPARE
+# define Py_TPFLAGS_HAVE_RICHCOMPARE 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_SEQUENCE_IN
+# define Py_TPFLAGS_HAVE_SEQUENCE_IN 0
+#endif
+
+#include <pyuno.hxx>
+
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/script/XInvocation.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <osl/module.hxx>
+
+namespace com::sun::star::beans { class XIntrospection; }
+namespace com::sun::star::container { class XEnumeration; }
+namespace com::sun::star::container { class XHierarchicalNameAccess; }
+namespace com::sun::star::lang { class XSingleServiceFactory; }
+namespace com::sun::star::reflection { class XIdlReflection; }
+namespace com::sun::star::script { class XInvocation2; }
+namespace com::sun::star::script { class XInvocationAdapterFactory2; }
+namespace com::sun::star::script { class XTypeConverter; }
+
+namespace pyuno
+{
+
+
+// Logging API - implementation can be found in pyuno_util
+
+struct RuntimeCargo;
+namespace LogLevel
+{
+// when you add a loglevel, extend the log function !
+const sal_Int32 NONE = 0;
+const sal_Int32 CALL = 1;
+const sal_Int32 ARGS = 2;
+}
+
+bool isLog( RuntimeCargo const *cargo, sal_Int32 loglevel );
+void log( RuntimeCargo *cargo, sal_Int32 level, std::u16string_view logString );
+void log( RuntimeCargo *cargo, sal_Int32 level, const char *str );
+void logCall( RuntimeCargo *cargo, const char *intro,
+ void * ptr, std::u16string_view aFunctionName,
+ const css::uno::Sequence< css::uno::Any > & args );
+void logReply( RuntimeCargo *cargo, const char *intro,
+ void * ptr, std::u16string_view aFunctionName,
+ const css::uno::Any &returnValue,
+ const css::uno::Sequence< css::uno::Any > & args );
+void logException( RuntimeCargo *cargo, const char *intro,
+ void * ptr, std::u16string_view aFunctionName,
+ const void * data, const css::uno::Type & type );
+const sal_Int32 VAL2STR_MODE_DEEP = 0;
+const sal_Int32 VAL2STR_MODE_SHALLOW = 1;
+OUString val2str( const void * pVal, typelib_TypeDescriptionReference * pTypeRef, sal_Int32 mode = VAL2STR_MODE_DEEP );
+
+
+typedef std::unordered_map
+<
+ PyRef,
+ css::uno::WeakReference< css::script::XInvocation >,
+ PyRef::Hash
+> PyRef2Adapter;
+
+
+typedef std::unordered_map
+<
+ OUString,
+ PyRef
+> ExceptionClassMap;
+
+typedef std::unordered_map
+<
+ OUString,
+ css::uno::Sequence< sal_Int16 >
+> MethodOutIndexMap;
+
+typedef std::unordered_set< PyRef , PyRef::Hash > ClassSet;
+
+int PyUNO_initType();
+int PyUNOStruct_initType();
+
+PyRef PyUNO_new (
+ const css::uno::Any & targetInterface,
+ const css::uno::Reference<css::lang::XSingleServiceFactory> & ssf );
+
+PyRef PyUNOStruct_new (
+ const css::uno::Any &targetInterface,
+ const css::uno::Reference<css::lang::XSingleServiceFactory> &ssf );
+
+struct PyUNOInternals
+{
+ css::uno::Reference <css::script::XInvocation2> xInvocation;
+ css::uno::Any wrappedObject;
+};
+
+typedef struct
+{
+ PyObject_HEAD
+ PyUNOInternals* members;
+} PyUNO;
+
+PyObject* PyUNO_iterator_new (
+ const css::uno::Reference<css::container::XEnumeration>& xEnumeration);
+
+struct PyUNO_iterator_Internals
+{
+ css::uno::Reference <css::container::XEnumeration> xEnumeration;
+};
+
+typedef struct
+{
+ PyObject_HEAD
+ PyUNO_iterator_Internals* members;
+} PyUNO_iterator;
+
+PyObject* PyUNO_list_iterator_new (
+ const css::uno::Reference<css::container::XIndexAccess> &xIndexAccess);
+
+struct PyUNO_list_iterator_Internals
+{
+ css::uno::Reference <css::container::XIndexAccess> xIndexAccess;
+ int index;
+};
+
+typedef struct
+{
+ PyObject_HEAD
+ PyUNO_list_iterator_Internals* members;
+} PyUNO_list_iterator;
+
+PyRef ustring2PyUnicode( const OUString &source );
+PyRef ustring2PyString( std::u16string_view source );
+OUString pyString2ustring( PyObject *str );
+
+/// @throws css::reflection::InvocationTargetException
+/// @throws css::uno::RuntimeException
+void raiseInvocationTargetExceptionWhenNeeded( const Runtime &runtime );
+
+PyRef PyUNO_callable_new (
+ const css::uno::Reference<css::script::XInvocation2> &xInv,
+ const OUString &methodName,
+ ConversionMode mode = REJECT_UNO_ANY );
+
+PyObject* PyUNO_Type_new (const char *typeName , css::uno::TypeClass t , const Runtime &r );
+PyObject* PyUNO_Enum_new( const char *enumBase, const char *enumValue, const Runtime &r );
+PyObject* PyUNO_char_new (sal_Unicode c , const Runtime &r);
+PyObject *PyUNO_ByteSequence_new( const css::uno::Sequence< sal_Int8 > &, const Runtime &r );
+
+PyRef getTypeClass( const Runtime &);
+PyRef getEnumClass( const Runtime &);
+PyRef getCharClass( const Runtime &);
+PyRef getByteSequenceClass( const Runtime & );
+PyRef getPyUnoClass();
+PyRef getPyUnoStructClass();
+PyRef getClass( const OUString & name , const Runtime & runtime );
+PyRef getAnyClass( const Runtime &);
+PyObject *PyUNO_invoke( PyObject *object, const char *name , PyObject *args );
+
+/// @throws css::uno::RuntimeException
+css::uno::Any PyEnum2Enum( PyObject *obj );
+/// @throws css::uno::RuntimeException
+sal_Unicode PyChar2Unicode( PyObject *o );
+/// @throws css::uno::RuntimeException
+css::uno::Type PyType2Type( PyObject * o );
+
+void raisePyExceptionWithAny( const css::uno::Any &a );
+const char *typeClassToString( css::uno::TypeClass t );
+
+/// @throws css::uno::RuntimeException
+PyRef getObjectFromUnoModule( const Runtime &runtime, const char * object );
+
+bool isInterfaceClass( const Runtime &, PyObject *obj );
+bool isInstanceOfStructOrException( PyObject *obj);
+
+struct RuntimeCargo
+{
+ css::uno::Reference< css::lang::XSingleServiceFactory > xInvocation;
+ css::uno::Reference< css::script::XTypeConverter> xTypeConverter;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::reflection::XIdlReflection > xCoreReflection;
+ css::uno::Reference< css::container::XHierarchicalNameAccess > xTdMgr;
+ css::uno::Reference< css::script::XInvocationAdapterFactory2 > xAdapterFactory;
+ css::uno::Reference< css::beans::XIntrospection > xIntrospection;
+ PyRef dictUnoModule;
+ osl::Module testModule;
+ bool valid;
+ ExceptionClassMap exceptionMap;
+ ClassSet interfaceSet;
+ PyRef2Adapter mappedObjects;
+ FILE *logFile;
+ sal_Int32 logLevel;
+
+ PyRef const & getUnoModule();
+};
+
+struct stRuntimeImpl
+{
+ PyObject_HEAD
+ struct RuntimeCargo *cargo;
+public:
+ static void del( PyObject *self );
+
+ /// @throws css::uno::RuntimeException
+ static PyRef create(
+ const css::uno::Reference< css::uno::XComponentContext > & xContext );
+};
+
+
+class Adapter : public cppu::WeakImplHelper<
+ css::script::XInvocation, css::lang::XUnoTunnel >
+{
+ PyRef mWrappedObject;
+ PyInterpreterState *mInterpreter; // interpreters don't seem to be refcounted !
+ css::uno::Sequence< css::uno::Type > mTypes;
+ MethodOutIndexMap m_methodOutIndexMap;
+
+private:
+ css::uno::Sequence< sal_Int16 > getOutIndexes( const OUString & functionName );
+
+public:
+public:
+ Adapter( PyRef obj,
+ const css::uno::Sequence< css::uno::Type > & types );
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+ const PyRef& getWrappedObject() const { return mWrappedObject; }
+ const css::uno::Sequence< css::uno::Type >& getWrappedTypes() const { return mTypes; }
+ virtual ~Adapter() override;
+
+ // XInvocation
+ virtual css::uno::Reference< css::beans::XIntrospectionAccess >
+ SAL_CALL getIntrospection( ) override;
+ virtual css::uno::Any SAL_CALL invoke(
+ const OUString& aFunctionName,
+ const css::uno::Sequence< css::uno::Any >& aParams,
+ css::uno::Sequence< sal_Int16 >& aOutParamIndex,
+ css::uno::Sequence< css::uno::Any >& aOutParam ) override;
+
+ virtual void SAL_CALL setValue(
+ const OUString& aPropertyName,
+ const css::uno::Any& aValue ) override;
+
+ virtual css::uno::Any SAL_CALL getValue( const OUString& aPropertyName ) override;
+ virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override;
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+};
+
+
+/** releases a refcount on the interpreter object and on another given python object.
+
+ The function can be called from any thread regardless of whether the global
+ interpreter lock is held.
+
+ */
+void decreaseRefCount( PyInterpreterState *interpreter, PyObject *object );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_iterator.cxx b/pyuno/source/module/pyuno_iterator.cxx
new file mode 100644
index 0000000000..134f318a10
--- /dev/null
+++ b/pyuno/source/module/pyuno_iterator.cxx
@@ -0,0 +1,343 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "pyuno_impl.hxx"
+
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::lang::IndexOutOfBoundsException;
+using com::sun::star::lang::WrappedTargetException;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::RuntimeException;
+
+
+namespace pyuno
+{
+
+static void PyUNO_iterator_del( PyObject* self )
+{
+ PyUNO_iterator* me = reinterpret_cast<PyUNO_iterator*>(self);
+
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del( self );
+}
+
+static PyObject* PyUNO_iterator_iter( PyObject *self )
+{
+ Py_INCREF( self );
+ return self;
+}
+
+static PyObject* PyUNO_iterator_next( PyObject *self )
+{
+ PyUNO_iterator* me = reinterpret_cast<PyUNO_iterator*>(self);
+
+ Runtime runtime;
+ Any aRet;
+
+ try
+ {
+ bool hasMoreElements = false;
+
+ {
+ PyThreadDetach antiguard;
+
+ hasMoreElements = me->members->xEnumeration->hasMoreElements();
+ if ( hasMoreElements )
+ {
+ aRet = me->members->xEnumeration->nextElement();
+ }
+ }
+
+ if ( hasMoreElements )
+ {
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ PyErr_SetString( PyExc_StopIteration, "" );
+ return nullptr;
+ }
+ catch( css::container::NoSuchElementException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return nullptr;
+}
+
+static PyTypeObject PyUNO_iterator_Type =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "PyUNO_iterator",
+ sizeof (PyUNO_iterator),
+ 0,
+ PyUNO_iterator_del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_ITER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ PyUNO_iterator_iter, // Generic, reused between the iterator types
+ PyUNO_iterator_next,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+PyObject* PyUNO_iterator_new( const Reference< XEnumeration >& xEnumeration )
+{
+ PyUNO_iterator* self = PyObject_New( PyUNO_iterator, &PyUNO_iterator_Type );
+ if ( self == nullptr )
+ return nullptr; // == error
+ self->members = new PyUNO_iterator_Internals;
+ self->members->xEnumeration = xEnumeration;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void PyUNO_list_iterator_del( PyObject* self )
+{
+ PyUNO_list_iterator* me = reinterpret_cast<PyUNO_list_iterator*>(self);
+
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del( self );
+}
+
+
+static PyObject* PyUNO_list_iterator_next( PyObject *self )
+{
+ PyUNO_list_iterator* me = reinterpret_cast<PyUNO_list_iterator*>(self);
+
+ Runtime runtime;
+ Any aRet;
+
+ try
+ {
+ bool noMoreElements = false;
+ {
+ PyThreadDetach antiguard;
+ try {
+ aRet = me->members->xIndexAccess->getByIndex( me->members->index );
+ }
+ catch( const css::lang::IndexOutOfBoundsException & )
+ {
+ noMoreElements = true;
+ }
+ }
+
+ if ( noMoreElements )
+ {
+ PyErr_SetString( PyExc_StopIteration, "" );
+ return nullptr;
+ }
+
+ PyRef rRet = runtime.any2PyObject( aRet );
+ me->members->index++;
+ return rRet.getAcquired();
+ }
+ catch( css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+ catch( const css::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( css::uno::Any( e ) );
+ }
+
+ return nullptr;
+}
+
+static PyTypeObject PyUNO_list_iterator_Type =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "PyUNO_iterator",
+ sizeof (PyUNO_list_iterator),
+ 0,
+ PyUNO_list_iterator_del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_ITER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ PyUNO_iterator_iter, // Generic, reused between the iterator types
+ PyUNO_list_iterator_next,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+PyObject* PyUNO_list_iterator_new( const Reference<XIndexAccess> &xIndexAccess )
+{
+ PyUNO_list_iterator* self = PyObject_New( PyUNO_list_iterator, &PyUNO_list_iterator_Type );
+ if ( self == nullptr )
+ return nullptr; // == error
+ self->members = new PyUNO_list_iterator_Internals;
+ self->members->xIndexAccess = xIndexAccess;
+ self->members->index = 0;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_module.cxx b/pyuno/source/module/pyuno_module.cxx
new file mode 100644
index 0000000000..793aac834b
--- /dev/null
+++ b/pyuno/source/module/pyuno_module.cxx
@@ -0,0 +1,896 @@
+/* -*- Mode: C++; eval:(c-set-style "bsd"); tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include "pyuno_impl.hxx"
+
+#include <cassert>
+#include <string_view>
+#include <unordered_map>
+
+#include <osl/module.hxx>
+#include <osl/thread.h>
+#include <osl/file.hxx>
+#include <sal/log.hxx>
+
+#include <typelib/typedescription.hxx>
+
+#include <rtl/ustring.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uuid.h>
+#include <rtl/bootstrap.hxx>
+
+#include <uno/current_context.hxx>
+#include <cppuhelper/bootstrap.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/reflection/XConstantTypeDescription.hpp>
+#include <com/sun/star/reflection/XIdlClass.hpp>
+#include <com/sun/star/registry/InvalidRegistryException.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+
+using osl::Module;
+
+
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::TypeDescription;
+using com::sun::star::uno::XComponentContext;
+using com::sun::star::container::NoSuchElementException;
+using com::sun::star::reflection::XIdlClass;
+using com::sun::star::script::XInvocation2;
+
+using namespace pyuno;
+
+namespace {
+
+/**
+ @ index of the next to be used member in the initializer list !
+ */
+// LEM TODO: export member names as keyword arguments in initialiser?
+// Python supports very flexible variadic functions. By marking
+// variables with one asterisk (e.g. *var) the given variable is
+// defined to be a tuple of all the extra arguments. By marking
+// variables with two asterisks (e.g. **var) the given variable is a
+// 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.
+
+class fillStructState
+{
+ // Keyword arguments used
+ PyObject *used;
+ // Which structure members are initialised
+ std::unordered_map <OUString, bool> initialised;
+ // How many positional arguments are consumed
+ // This is always the so-many first ones
+ sal_Int32 nPosConsumed;
+
+public:
+ fillStructState()
+ : used (PyDict_New())
+ , nPosConsumed (0)
+ {
+ if ( ! used )
+ throw RuntimeException("pyuno._createUnoStructHelper failed to create new dictionary");
+ }
+ ~fillStructState()
+ {
+ Py_DECREF(used);
+ }
+ void setUsed(PyObject *key)
+ {
+ PyDict_SetItem(used, key, Py_True);
+ }
+ void setInitialised(const OUString& key, sal_Int32 pos = -1)
+ {
+ if (initialised[key])
+ {
+ OUStringBuffer buf( "pyuno._createUnoStructHelper: member '" + key + "'");
+ if ( pos >= 0 )
+ {
+ buf.append( " at position " + OUString::number(pos));
+ }
+ buf.append( " initialised multiple times.");
+ throw RuntimeException(buf.makeStringAndClear());
+ }
+ initialised[key] = true;
+ if ( pos >= 0 )
+ ++nPosConsumed;
+ }
+ bool isInitialised(const OUString& key)
+ {
+ return initialised[key];
+ }
+ PyObject *getUsed() const
+ {
+ return used;
+ }
+ sal_Int32 getCntConsumed() const
+ {
+ return nPosConsumed;
+ }
+};
+
+/// @throws RuntimeException
+void fillStruct(
+ const Reference< XInvocation2 > &inv,
+ typelib_CompoundTypeDescription *pCompType,
+ PyObject *initializer,
+ PyObject *kwinitializer,
+ fillStructState &state,
+ const Runtime &runtime)
+{
+ if( pCompType->pBaseTypeDescription )
+ fillStruct( inv, pCompType->pBaseTypeDescription, initializer, kwinitializer, state, runtime );
+
+ const sal_Int32 nMembers = pCompType->nMembers;
+ {
+ for( int i = 0 ; i < nMembers ; i ++ )
+ {
+ const OUString OUMemberName (pCompType->ppMemberNames[i]);
+ PyObject *pyMemberName =
+ PyUnicode_FromString(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& rMemberName (pCompType->ppMemberNames[i]);
+ state.setInitialised(rMemberName, tupleIndex);
+ PyObject *element = PyTuple_GetItem( initializer, tupleIndex );
+ Any a = runtime.pyObject2Any( element, ACCEPT_UNO_ANY );
+ inv->setValue( rMemberName, a );
+ }
+ }
+ if ( PyTuple_Size( initializer ) <= 0 )
+ return;
+
+ // Allow partial initialisation when only keyword arguments are given
+ for ( int i = 0; i < nMembers ; ++i)
+ {
+ const OUString memberName (pCompType->ppMemberNames[i]);
+ if ( ! state.isInitialised( memberName ) )
+ {
+ OUString buf = "pyuno._createUnoStructHelper: member '" +
+ memberName +
+ "' of struct type '" +
+ OUString::unacquired(&pCompType->aBase.pTypeName) +
+ "' not given a value.";
+ throw RuntimeException(buf);
+ }
+ }
+}
+
+OUString getLibDir()
+{
+ static OUString sLibDir = []() {
+ OUString libDir;
+
+ // workarounds the $(ORIGIN) until it is available
+ if (Module::getUrlFromAddress(reinterpret_cast<oslGenericFunction>(getLibDir), libDir))
+ {
+ libDir = libDir.copy(0, libDir.lastIndexOf('/'));
+ OUString name("PYUNOLIBDIR");
+ rtl_bootstrap_set(name.pData, libDir.pData);
+ }
+ return libDir;
+ }();
+
+ return sLibDir;
+}
+
+void raisePySystemException( const char * exceptionType, std::u16string_view message )
+{
+ OString buf = OString::Concat("Error during bootstrapping uno (") +
+ exceptionType +
+ "):" +
+ OUStringToOString( message, osl_getThreadTextEncoding() );
+ PyErr_SetString( PyExc_SystemError, buf.getStr() );
+}
+
+extern "C" {
+
+static PyObject* getComponentContext(
+ SAL_UNUSED_PARAMETER PyObject*, SAL_UNUSED_PARAMETER PyObject*)
+{
+ PyRef ret;
+ try
+ {
+ Reference<XComponentContext> ctx;
+
+ // getLibDir() must be called in order to set bootstrap variables correctly !
+ OUString path( getLibDir());
+ if( Runtime::isInitialized() )
+ {
+ Runtime runtime;
+ ctx = runtime.getImpl()->cargo->xContext;
+ }
+ else
+ {
+ if( path.isEmpty() )
+ {
+ PyErr_SetString(
+ PyExc_RuntimeError, "osl_getUrlFromAddress fails, that's why I cannot find ini "
+ "file for bootstrapping python uno bridge\n" );
+ return nullptr;
+ }
+
+ OUString iniFile = path +
+#ifdef MACOSX
+ "/../" LIBO_ETC_FOLDER
+#endif
+ "/" SAL_CONFIGFILE( "pyuno" );
+ osl::DirectoryItem item;
+ if( osl::DirectoryItem::get( iniFile, item ) == osl::FileBase::E_None )
+ {
+ // in case pyuno.ini exists, use this file for bootstrapping
+ PyThreadDetach antiguard;
+ ctx = cppu::defaultBootstrap_InitialComponentContext (iniFile);
+ }
+ else
+ {
+ // defaulting to the standard bootstrapping
+ PyThreadDetach antiguard;
+ ctx = cppu::defaultBootstrap_InitialComponentContext ();
+ }
+
+ }
+
+ if( ! Runtime::isInitialized() )
+ {
+ Runtime::initialize( ctx );
+ }
+ Runtime runtime;
+ ret = runtime.any2PyObject( Any( ctx ) );
+ }
+ catch (const css::registry::InvalidRegistryException &e)
+ {
+ // can't use raisePyExceptionWithAny() here, because the function
+ // does any conversions, which will not work with a
+ // wrongly bootstrapped pyuno!
+ raisePySystemException( "InvalidRegistryException", e.Message );
+ }
+ catch(const css::lang::IllegalArgumentException & e)
+ {
+ raisePySystemException( "IllegalArgumentException", e.Message );
+ }
+ catch(const css::script::CannotConvertException & e)
+ {
+ raisePySystemException( "CannotConvertException", e.Message );
+ }
+ catch (const css::uno::RuntimeException & e)
+ {
+ raisePySystemException( "RuntimeException", e.Message );
+ }
+ catch (const css::uno::Exception & e)
+ {
+ raisePySystemException( "uno::Exception", e.Message );
+ }
+ return ret.getAcquired();
+}
+
+// While pyuno.private_initTestEnvironment is called from individual Python tests (e.g., from
+// UnoInProcess in unotest/source/python/org/libreoffice/unotest.py, which makes sure to call it
+// only once), pyuno.private_deinitTestEnvironment is called centrally from
+// unotest/source/python/org/libreoffice/unittest.py at the end of every PythonTest (to DeInitVCL
+// exactly once near the end of the process, if InitVCL has ever been called via
+// pyuno.private_initTestEnvironment):
+
+osl::Module * testModule = nullptr;
+
+static PyObject* initTestEnvironment(
+ SAL_UNUSED_PARAMETER PyObject*, SAL_UNUSED_PARAMETER PyObject*)
+{
+ // this tries to bootstrap enough of the soffice from python to run
+ // unit tests, which is only possible indirectly because pyuno is URE
+ // so load "test" library and invoke a function there to do the work
+ assert(testModule == nullptr);
+ try
+ {
+ PyObject *const ctx(getComponentContext(nullptr, nullptr));
+ if (!ctx) { abort(); }
+ Runtime const runtime;
+ Any const a(runtime.pyObject2Any(ctx));
+ Reference<XComponentContext> xContext;
+ a >>= xContext;
+ if (!xContext.is()) { abort(); }
+ using css::lang::XMultiServiceFactory;
+ Reference<XMultiServiceFactory> const xMSF(
+ xContext->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ char *const testlib = getenv("TEST_LIB");
+ if (!testlib) { abort(); }
+#ifdef _WIN32
+ OString const libname = OString(testlib, strlen(testlib))
+ .replaceAll(OString('/'), OString('\\'));
+#else
+ OString const libname(testlib, strlen(testlib));
+#endif
+
+ osl::Module &mod = runtime.getImpl()->cargo->testModule;
+ mod.load(OStringToOUString(libname, osl_getThreadTextEncoding()),
+ SAL_LOADMODULE_LAZY | SAL_LOADMODULE_GLOBAL);
+ if (!mod.is()) { abort(); }
+ oslGenericFunction const pFunc(
+ mod.getFunctionSymbol("test_init"));
+ if (!pFunc) { abort(); }
+ reinterpret_cast<void (SAL_CALL *)(XMultiServiceFactory*)>(pFunc)(xMSF.get());
+ testModule = &mod;
+ }
+ catch (const css::uno::Exception &)
+ {
+ abort();
+ }
+ return Py_None;
+}
+
+static PyObject* deinitTestEnvironment(
+ SAL_UNUSED_PARAMETER PyObject*, SAL_UNUSED_PARAMETER PyObject*)
+{
+ if (testModule != nullptr)
+ {
+ try
+ {
+ oslGenericFunction const pFunc(
+ testModule->getFunctionSymbol("test_deinit"));
+ if (!pFunc) { abort(); }
+ reinterpret_cast<void (SAL_CALL *)()>(pFunc)();
+ }
+ catch (const css::uno::Exception &)
+ {
+ abort();
+ }
+ }
+ return Py_None;
+}
+
+PyObject * extractOneStringArg( PyObject *args, char const *funcName )
+{
+ if( !PyTuple_Check( args ) || PyTuple_Size( args) != 1 )
+ {
+ OString buf = funcName + OString::Concat(": expecting one string argument");
+ PyErr_SetString( PyExc_RuntimeError, buf.getStr() );
+ return nullptr;
+ }
+ PyObject *obj = PyTuple_GetItem( args, 0 );
+ if (!PyUnicode_Check(obj))
+ {
+ OString buf = funcName + OString::Concat(": expecting one string argument");
+ PyErr_SetString( PyExc_TypeError, buf.getStr());
+ return nullptr;
+ }
+ return obj;
+}
+
+static PyObject *createUnoStructHelper(
+ SAL_UNUSED_PARAMETER PyObject *, PyObject* args, PyObject* keywordArgs)
+{
+ Any IdlStruct;
+ PyRef ret;
+ try
+ {
+ Runtime runtime;
+ if( PyTuple_Size( args ) == 2 )
+ {
+ PyObject *structName = PyTuple_GetItem(args, 0);
+ PyObject *initializer = PyTuple_GetItem(args, 1);
+
+ if (PyUnicode_Check(structName))
+ {
+ if( PyTuple_Check( initializer ) && PyDict_Check ( keywordArgs ) )
+ {
+ OUString typeName( OUString::createFromAscii(PyUnicode_AsUTF8(structName)));
+ RuntimeCargo *c = runtime.getImpl()->cargo;
+ Reference<XIdlClass> idl_class = c->xCoreReflection->forName (typeName);
+ if (idl_class.is ())
+ {
+ idl_class->createObject (IdlStruct);
+ PyRef returnCandidate( PyUNOStruct_new( IdlStruct, c->xInvocation ) );
+ PyUNO *me = reinterpret_cast<PyUNO*>( returnCandidate.get() );
+ TypeDescription desc( typeName );
+ OSL_ASSERT( desc.is() ); // could already instantiate an XInvocation2 !
+
+ typelib_CompoundTypeDescription *pCompType =
+ reinterpret_cast<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) )
+ {
+ throw RuntimeException( "pyuno._createUnoStructHelper: too many "
+ "elements in the initializer list, expected " +
+ OUString::number(state.getCntConsumed()) + ", got " +
+ OUString::number( PyTuple_Size(initializer) ) );
+ }
+ ret = PyRef( PyTuple_Pack(2, returnCandidate.get(), state.getUsed()), SAL_NO_ACQUIRE);
+ }
+ else
+ {
+ OString buf = OString::Concat("UNO struct ")
+ + PyUnicode_AsUTF8(structName)
+ + " is unknown";
+ PyErr_SetString (PyExc_RuntimeError, buf.getStr());
+ }
+ }
+ else
+ {
+ PyErr_SetString(
+ PyExc_RuntimeError,
+ "pyuno._createUnoStructHelper: 2nd argument (initializer sequence) is no tuple" );
+ }
+ }
+ else
+ {
+ PyErr_SetString (PyExc_AttributeError, "createUnoStruct: first argument wasn't a string");
+ }
+ }
+ else
+ {
+ PyErr_SetString (PyExc_AttributeError, "pyuno._createUnoStructHelper: expects exactly two non-keyword arguments:\n\tStructure Name\n\tinitialiser tuple; may be the empty tuple");
+ }
+ }
+ catch( const css::uno::RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ catch( const css::script::CannotConvertException & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ catch( const css::uno::Exception & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ return ret.getAcquired();
+}
+
+static PyObject *getTypeByName(
+ SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+ PyObject * ret = nullptr;
+
+ try
+ {
+ char *name;
+
+ if (PyArg_ParseTuple (args, "s", &name))
+ {
+ OUString typeName ( OUString::createFromAscii( name ) );
+ TypeDescription typeDesc( typeName );
+ if( typeDesc.is() )
+ {
+ Runtime runtime;
+ ret = PyUNO_Type_new(
+ name, static_cast<css::uno::TypeClass>(typeDesc.get()->eTypeClass), runtime );
+ }
+ else
+ {
+ OString buf = OString::Concat("Type ") + name + " is unknown";
+ PyErr_SetString( PyExc_RuntimeError, buf.getStr() );
+ }
+ }
+ }
+ catch ( const RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ return ret;
+}
+
+static PyObject *getConstantByName(
+ SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+ PyObject *ret = nullptr;
+ try
+ {
+ char *name;
+
+ if (PyArg_ParseTuple (args, "s", &name))
+ {
+ OUString typeName ( OUString::createFromAscii( name ) );
+ Runtime runtime;
+ css::uno::Reference< css::reflection::XConstantTypeDescription > td;
+ if (!(runtime.getImpl()->cargo->xTdMgr->getByHierarchicalName(
+ typeName)
+ >>= td))
+ {
+ throw RuntimeException( "pyuno.getConstantByName: " + typeName + "is not a constant" );
+ }
+ PyRef constant = runtime.any2PyObject( td->getConstantValue() );
+ ret = constant.getAcquired();
+ }
+ }
+ catch( const NoSuchElementException & e )
+ {
+ // to the python programmer, this is a runtime exception,
+ // do not support tweakings with the type system
+ RuntimeException runExc( e.Message );
+ raisePyExceptionWithAny( Any( runExc ) );
+ }
+ catch(const css::script::CannotConvertException & e)
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ catch(const css::lang::IllegalArgumentException & e)
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ catch( const RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ return ret;
+}
+
+static PyObject *checkType( SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+ if( !PyTuple_Check( args ) || PyTuple_Size( args) != 1 )
+ {
+ OString buf = "pyuno.checkType : expecting one uno.Type argument"_ostr;
+ PyErr_SetString( PyExc_RuntimeError, buf.getStr() );
+ return nullptr;
+ }
+ PyObject *obj = PyTuple_GetItem( args, 0 );
+
+ try
+ {
+ PyType2Type( obj );
+ }
+ catch(const RuntimeException & e)
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ return nullptr;
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+static PyObject *checkEnum( SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+ if( !PyTuple_Check( args ) || PyTuple_Size( args) != 1 )
+ {
+ OString buf = "pyuno.checkType : expecting one uno.Type argument"_ostr;
+ PyErr_SetString( PyExc_RuntimeError, buf.getStr() );
+ return nullptr;
+ }
+ PyObject *obj = PyTuple_GetItem( args, 0 );
+
+ try
+ {
+ PyEnum2Enum( obj );
+ }
+ catch(const RuntimeException & e)
+ {
+ raisePyExceptionWithAny( Any( e) );
+ return nullptr;
+ }
+ Py_INCREF( Py_None );
+ return Py_None;
+}
+
+static PyObject *getClass( SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+ PyObject *obj = extractOneStringArg( args, "pyuno.getClass");
+ if( ! obj )
+ return nullptr;
+
+ try
+ {
+ Runtime runtime;
+ PyRef ret = getClass(pyString2ustring(obj), runtime);
+ Py_XINCREF( ret.get() );
+ return ret.get();
+ }
+ catch(const RuntimeException & e)
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ return nullptr;
+}
+
+static PyObject *isInterface( SAL_UNUSED_PARAMETER PyObject *, PyObject *args )
+{
+
+ if( PyTuple_Check( args ) && PyTuple_Size( args ) == 1 )
+ {
+ PyObject *obj = PyTuple_GetItem( args, 0 );
+ Runtime r;
+ return PyLong_FromLong( isInterfaceClass( r, obj ) );
+ }
+ return PyLong_FromLong( 0 );
+}
+
+static PyObject * generateUuid(
+ SAL_UNUSED_PARAMETER PyObject *, SAL_UNUSED_PARAMETER PyObject * )
+{
+ Sequence< sal_Int8 > seq( 16 );
+ rtl_createUuid( reinterpret_cast<sal_uInt8*>(seq.getArray()) , nullptr , false );
+ PyRef ret;
+ try
+ {
+ Runtime runtime;
+ ret = runtime.any2PyObject( Any( seq ) );
+ }
+ catch( const RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ return ret.getAcquired();
+}
+
+static PyObject *systemPathToFileUrl(
+ SAL_UNUSED_PARAMETER PyObject *, PyObject * args )
+{
+ PyObject *obj = extractOneStringArg( args, "pyuno.systemPathToFileUrl" );
+ if( ! obj )
+ return nullptr;
+
+ OUString sysPath = pyString2ustring( obj );
+ OUString url;
+ osl::FileBase::RC e = osl::FileBase::getFileURLFromSystemPath( sysPath, url );
+
+ if( e != osl::FileBase::E_None )
+ {
+ OUString buf = "Couldn't convert " +
+ sysPath +
+ " to a file url for reason (" +
+ OUString::number( static_cast<sal_Int32>(e) ) +
+ ")";
+ raisePyExceptionWithAny(
+ Any( RuntimeException( buf )));
+ return nullptr;
+ }
+ return ustring2PyUnicode( url ).getAcquired();
+}
+
+static PyObject * fileUrlToSystemPath(
+ SAL_UNUSED_PARAMETER PyObject *, PyObject * args )
+{
+ PyObject *obj = extractOneStringArg( args, "pyuno.fileUrlToSystemPath" );
+ if( ! obj )
+ return nullptr;
+
+ OUString url = pyString2ustring( obj );
+ OUString sysPath;
+ osl::FileBase::RC e = osl::FileBase::getSystemPathFromFileURL( url, sysPath );
+
+ if( e != osl::FileBase::E_None )
+ {
+ OUString buf = "Couldn't convert file url " +
+ sysPath +
+ " to a system path for reason (" +
+ OUString::number( static_cast<sal_Int32>(e) ) +
+ ")";
+ raisePyExceptionWithAny(
+ Any( RuntimeException( buf )));
+ return nullptr;
+ }
+ return ustring2PyUnicode( sysPath ).getAcquired();
+}
+
+static PyObject * absolutize( SAL_UNUSED_PARAMETER PyObject *, PyObject * args )
+{
+ if( !PyTuple_Check( args ) || PyTuple_Size( args ) != 2 )
+ return nullptr;
+
+ OUString ouPath = pyString2ustring( PyTuple_GetItem( args , 0 ) );
+ OUString ouRel = pyString2ustring( PyTuple_GetItem( args, 1 ) );
+ OUString ret;
+ oslFileError e = osl_getAbsoluteFileURL( ouPath.pData, ouRel.pData, &(ret.pData) );
+ if( e != osl_File_E_None )
+ {
+ OUString buf =
+ "Couldn't absolutize " +
+ ouRel +
+ " using root " +
+ ouPath +
+ " for reason (" +
+ OUString::number(static_cast<sal_Int32>(e) ) +
+ ")";
+
+ PyErr_SetString(
+ PyExc_OSError,
+ OUStringToOString(buf,osl_getThreadTextEncoding()).getStr());
+ return nullptr;
+ }
+ return ustring2PyUnicode( ret ).getAcquired();
+}
+
+static PyObject * invoke(SAL_UNUSED_PARAMETER PyObject *, PyObject *args)
+{
+ PyObject *ret = nullptr;
+ if(PyTuple_Check(args) && PyTuple_Size(args) == 3)
+ {
+ PyObject *object = PyTuple_GetItem(args, 0);
+ PyObject *item1 = PyTuple_GetItem(args, 1);
+ if (PyUnicode_Check(item1))
+ {
+ const char *name = PyUnicode_AsUTF8(item1);
+ PyObject *item2 = PyTuple_GetItem(args, 2);
+ if(PyTuple_Check(item2))
+ {
+ ret = PyUNO_invoke(object, name, item2);
+ }
+ else
+ {
+ OString buf = OString::Concat("uno.invoke expects a tuple as 3rd argument, got ")
+ + PyUnicode_AsUTF8(PyObject_Str(item2));
+ PyErr_SetString(
+ PyExc_RuntimeError, buf.getStr());
+ }
+ }
+ else
+ {
+ OString buf = OString::Concat("uno.invoke expected a string as 2nd argument, got ")
+ + PyUnicode_AsUTF8(PyObject_Str(item1));
+ PyErr_SetString(
+ PyExc_RuntimeError, buf.getStr());
+ }
+ }
+ else
+ {
+ OString buf = "uno.invoke expects object, name, (arg1, arg2, ... )\n"_ostr;
+ PyErr_SetString(PyExc_RuntimeError, buf.getStr());
+ }
+ return ret;
+}
+
+static PyObject *getCurrentContext(
+ SAL_UNUSED_PARAMETER PyObject *, SAL_UNUSED_PARAMETER PyObject * )
+{
+ PyRef ret;
+ try
+ {
+ Runtime runtime;
+ ret = runtime.any2PyObject(
+ Any( css::uno::getCurrentContext() ) );
+ }
+ catch( const css::uno::Exception & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ return ret.getAcquired();
+}
+
+static PyObject *setCurrentContext(
+ SAL_UNUSED_PARAMETER PyObject *, SAL_UNUSED_PARAMETER PyObject * args )
+{
+ PyRef ret;
+ try
+ {
+ if( PyTuple_Check( args ) && PyTuple_Size( args ) == 1 )
+ {
+
+ Runtime runtime;
+ Any a = runtime.pyObject2Any( PyTuple_GetItem( args, 0 ) );
+
+ Reference< css::uno::XCurrentContext > context;
+
+ if( (a.hasValue() && (a >>= context)) || ! a.hasValue() )
+ {
+ ret = css::uno::setCurrentContext( context ) ? Py_True : Py_False;
+ }
+ else
+ {
+ OString buf =
+ OString::Concat("uno.setCurrentContext expects an XComponentContext implementation, got ")
+ + PyUnicode_AsUTF8(PyObject_Str(PyTuple_GetItem(args, 0)));
+ PyErr_SetString(
+ PyExc_RuntimeError, buf.getStr() );
+ }
+ }
+ else
+ {
+ OString buf = "uno.setCurrentContext expects exactly one argument (the current Context)\n"_ostr;
+ PyErr_SetString(
+ PyExc_RuntimeError, buf.getStr() );
+ }
+ }
+ catch( const css::uno::Exception & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+ return ret.getAcquired();
+}
+
+static PyObject *sal_debug(
+ SAL_UNUSED_PARAMETER PyObject *, SAL_UNUSED_PARAMETER PyObject * args )
+{
+ Py_INCREF( Py_None );
+ if( !PyTuple_Check( args ) || PyTuple_Size( args) != 1 )
+ return Py_None;
+
+ OUString line = pyString2ustring( PyTuple_GetItem( args, 0 ) );
+
+ SAL_DEBUG(line);
+
+ return Py_None;
+}
+
+}
+
+struct PyMethodDef PyUNOModule_methods [] =
+{
+ {"private_initTestEnvironment", initTestEnvironment, METH_VARARGS, nullptr},
+ {"private_deinitTestEnvironment", deinitTestEnvironment, METH_VARARGS, nullptr},
+ {"getComponentContext", getComponentContext, METH_VARARGS, nullptr},
+ {"_createUnoStructHelper", reinterpret_cast<PyCFunction>(createUnoStructHelper), METH_VARARGS | METH_KEYWORDS, nullptr},
+ {"getTypeByName", getTypeByName, METH_VARARGS, nullptr},
+ {"getConstantByName", getConstantByName, METH_VARARGS, nullptr},
+ {"getClass", getClass, METH_VARARGS, nullptr},
+ {"checkEnum", checkEnum, METH_VARARGS, nullptr},
+ {"checkType", checkType, METH_VARARGS, nullptr},
+ {"generateUuid", generateUuid, METH_VARARGS, nullptr},
+ {"systemPathToFileUrl", systemPathToFileUrl, METH_VARARGS, nullptr},
+ {"fileUrlToSystemPath", fileUrlToSystemPath, METH_VARARGS, nullptr},
+ {"absolutize", absolutize, METH_VARARGS | METH_KEYWORDS, nullptr},
+ {"isInterface", isInterface, METH_VARARGS, nullptr},
+ {"invoke", invoke, METH_VARARGS | METH_KEYWORDS, nullptr},
+ {"setCurrentContext", setCurrentContext, METH_VARARGS, nullptr},
+ {"getCurrentContext", getCurrentContext, METH_VARARGS, nullptr},
+ {"sal_debug", sal_debug, METH_VARARGS, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+}
+
+extern "C"
+PyObject* PyInit_pyuno()
+{
+ PyUNO_initType();
+ PyUNOStruct_initType();
+ // noop when called already, otherwise needed to allow multiple threads
+#if PY_VERSION_HEX < 0x03090000
+ PyEval_InitThreads();
+#endif
+ static struct PyModuleDef moduledef =
+ {
+ PyModuleDef_HEAD_INIT,
+ "pyuno", // module name
+ nullptr, // module documentation
+ -1, // module keeps state in global variables,
+ PyUNOModule_methods, // modules methods
+ nullptr, // m_reload (must be 0)
+ nullptr, // m_traverse
+ nullptr, // m_clear
+ nullptr, // m_free
+ };
+ return PyModule_Create(&moduledef);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_runtime.cxx b/pyuno/source/module/pyuno_runtime.cxx
new file mode 100644
index 0000000000..cb95e5a46d
--- /dev/null
+++ b/pyuno/source/module/pyuno_runtime.cxx
@@ -0,0 +1,1012 @@
+/* -*- Mode: C++; eval:(c-set-style "bsd"); tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <config_folders.h>
+
+#include "pyuno_impl.hxx"
+
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <osl/module.h>
+#include <osl/process.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ref.hxx>
+
+#include <typelib/typedescription.hxx>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/script/InvocationAdapterFactory.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <vector>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::XInterface;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::TypeDescription;
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Type;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::Exception;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::XComponentContext;
+using com::sun::star::lang::WrappedTargetRuntimeException;
+using com::sun::star::lang::XSingleServiceFactory;
+using com::sun::star::lang::XUnoTunnel;
+using com::sun::star::reflection::theCoreReflection;
+using com::sun::star::reflection::InvocationTargetException;
+using com::sun::star::script::Converter;
+using com::sun::star::script::XTypeConverter;
+using com::sun::star::script::XInvocation;
+using com::sun::star::beans::XMaterialHolder;
+using com::sun::star::beans::theIntrospection;
+
+namespace pyuno
+{
+
+static PyTypeObject RuntimeImpl_Type =
+{
+ PyVarObject_HEAD_INIT (&PyType_Type, 0)
+ "pyuno_runtime",
+ sizeof (RuntimeImpl),
+ 0,
+ RuntimeImpl::del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+ , 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+/*----------------------------------------------------------------------
+ Runtime implementation
+ -----------------------------------------------------------------------*/
+/// @throws css::uno::RuntimeException
+static void getRuntimeImpl( PyRef & globalDict, PyRef &runtimeImpl )
+{
+ PyThreadState * state = PyThreadState_Get();
+ if( ! state )
+ {
+ throw RuntimeException( "python global interpreter must be held (thread must be attached)" );
+ }
+
+ PyObject* pModule = PyImport_AddModule("__main__");
+
+ if (!pModule)
+ {
+ throw RuntimeException("can't import __main__ module");
+ }
+
+ globalDict = PyRef( PyModule_GetDict(pModule));
+
+ if( ! globalDict.is() ) // FATAL !
+ {
+ throw RuntimeException("can't find __main__ module");
+ }
+ runtimeImpl = PyDict_GetItemString( globalDict.get() , "pyuno_runtime" );
+}
+
+/// @throws RuntimeException
+static PyRef importUnoModule( )
+{
+ // import the uno module
+ PyRef module( PyImport_ImportModule( "uno" ), SAL_NO_ACQUIRE, NOT_NULL );
+ if( PyErr_Occurred() )
+ {
+ PyRef excType, excValue, excTraceback;
+ PyErr_Fetch( reinterpret_cast<PyObject **>(&excType), reinterpret_cast<PyObject**>(&excValue), reinterpret_cast<PyObject**>(&excTraceback));
+ // As of Python 2.7 this gives a rather non-useful "<traceback object at 0xADDRESS>",
+ // but it is the best we can do in the absence of uno._uno_extract_printable_stacktrace
+ // Who knows, a future Python might print something better.
+ PyRef str( PyObject_Str( excTraceback.get() ), SAL_NO_ACQUIRE );
+
+ OUStringBuffer buf;
+ buf.append( "python object raised an unknown exception (" );
+ PyRef valueRep( PyObject_Repr( excValue.get() ), SAL_NO_ACQUIRE );
+ buf.appendAscii( PyUnicode_AsUTF8( valueRep.get())).append( ", traceback follows\n" );
+ buf.appendAscii( PyUnicode_AsUTF8( str.get() ) );
+ buf.append( ")" );
+ throw RuntimeException( buf.makeStringAndClear() );
+ }
+ PyRef dict( PyModule_GetDict( module.get() ) );
+ return dict;
+}
+
+static void readLoggingConfig( sal_Int32 *pLevel, FILE **ppFile )
+{
+ *pLevel = LogLevel::NONE;
+ *ppFile = nullptr;
+ OUString fileName;
+ osl_getModuleURLFromFunctionAddress(
+ reinterpret_cast< oslGenericFunction >(readLoggingConfig),
+ &fileName.pData );
+ fileName = fileName.copy( fileName.lastIndexOf( '/' )+1 );
+#ifdef MACOSX
+ fileName += "../" LIBO_ETC_FOLDER "/";
+#endif
+ fileName += SAL_CONFIGFILE("pyuno" );
+ rtl::Bootstrap bootstrapHandle( fileName );
+
+ OUString str;
+ if( bootstrapHandle.getFrom( "PYUNO_LOGLEVEL", str ) )
+ {
+ if ( str == "NONE" )
+ *pLevel = LogLevel::NONE;
+ else if ( str == "CALL" )
+ *pLevel = LogLevel::CALL;
+ else if ( str == "ARGS" )
+ *pLevel = LogLevel::ARGS;
+ else
+ {
+ fprintf( stderr, "unknown loglevel %s\n",
+ OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ }
+ if( *pLevel <= LogLevel::NONE )
+ return;
+
+ *ppFile = stdout;
+ if( !bootstrapHandle.getFrom( "PYUNO_LOGTARGET", str ) )
+ return;
+
+ if ( str == "stdout" )
+ *ppFile = stdout;
+ else if ( str == "stderr" )
+ *ppFile = stderr;
+ else
+ {
+ osl_getSystemPathFromFileURL( str.pData, &str.pData);
+ OString o = OUStringToOString( str, osl_getThreadTextEncoding() );
+
+ oslProcessInfo data;
+ data.Size = sizeof( data );
+ if (osl_getProcessInfo(
+ nullptr , osl_Process_IDENTIFIER , &data ) == osl_Process_E_None)
+ {
+ o += ".";
+ o += OString::number(data.Ident);
+ }
+
+ *ppFile = fopen( o.getStr() , "w" );
+ if ( *ppFile )
+ {
+ // do not buffer (useful if e.g. analyzing a crash)
+ setvbuf( *ppFile, nullptr, _IONBF, 0 );
+ }
+ else
+ {
+ fprintf( stderr, "couldn't create file %s\n",
+ OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ }
+ }
+}
+
+/*-------------------------------------------------------------------
+ RuntimeImpl implementations
+ *-------------------------------------------------------------------*/
+PyRef stRuntimeImpl::create( const Reference< XComponentContext > &ctx )
+{
+ RuntimeImpl *me = PyObject_New (RuntimeImpl, &RuntimeImpl_Type);
+ if( ! me )
+ throw RuntimeException( "cannot instantiate pyuno::RuntimeImpl" );
+ me->cargo = nullptr;
+ // must use a different struct here, as the PyObject_New
+ // makes C++ unusable
+ RuntimeCargo *c = new RuntimeCargo;
+ readLoggingConfig( &(c->logLevel) , &(c->logFile) );
+ log( c, LogLevel::CALL, "Instantiating pyuno bridge" );
+
+ c->valid = true;
+ c->xContext = ctx;
+ c->xInvocation = Reference< XSingleServiceFactory > (
+ ctx->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.Invocation",
+ ctx ),
+ css::uno::UNO_QUERY_THROW );
+
+ c->xTypeConverter = Converter::create(ctx);
+ if( ! c->xTypeConverter.is() )
+ throw RuntimeException( "pyuno: couldn't instantiate typeconverter service" );
+
+ c->xCoreReflection = theCoreReflection::get(ctx);
+
+ c->xAdapterFactory = css::script::InvocationAdapterFactory::create(ctx);
+
+ c->xIntrospection = theIntrospection::get(ctx);
+
+ Any a = ctx->getValueByName("/singletons/com.sun.star.reflection.theTypeDescriptionManager");
+ a >>= c->xTdMgr;
+ if( ! c->xTdMgr.is() )
+ throw RuntimeException( "pyuno: couldn't retrieve typedescriptionmanager" );
+
+ me->cargo =c;
+ return PyRef( reinterpret_cast< PyObject * > ( me ), SAL_NO_ACQUIRE );
+}
+
+void stRuntimeImpl::del(PyObject* self)
+{
+ RuntimeImpl *me = reinterpret_cast< RuntimeImpl * > ( self );
+ if( me->cargo->logFile )
+ fclose( me->cargo->logFile );
+ delete me->cargo;
+ PyObject_Del (self);
+}
+
+
+void Runtime::initialize( const Reference< XComponentContext > & ctx )
+{
+ PyRef globalDict, runtime;
+ getRuntimeImpl( globalDict , runtime );
+ RuntimeImpl *impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
+
+ if( runtime.is() && impl->cargo->valid )
+ {
+ throw RuntimeException("pyuno runtime has already been initialized before" );
+ }
+ PyRef keep( RuntimeImpl::create( ctx ) );
+ PyDict_SetItemString( globalDict.get(), "pyuno_runtime" , keep.get() );
+ Py_XINCREF( keep.get() );
+}
+
+
+bool Runtime::isInitialized()
+{
+ PyRef globalDict, runtime;
+ getRuntimeImpl( globalDict , runtime );
+ RuntimeImpl *impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
+ return runtime.is() && impl->cargo->valid;
+}
+
+Runtime::Runtime()
+ : impl( nullptr )
+{
+ PyRef globalDict, runtime;
+ getRuntimeImpl( globalDict , runtime );
+ if( ! runtime.is() )
+ {
+ throw RuntimeException(
+ "pyuno runtime is not initialized, "
+ "(the pyuno.bootstrap needs to be called before using any uno classes)" );
+ }
+ impl = reinterpret_cast< RuntimeImpl * > (runtime.get());
+ Py_XINCREF( runtime.get() );
+}
+
+Runtime::Runtime( const Runtime & r )
+{
+ impl = r.impl;
+ Py_XINCREF( reinterpret_cast< PyObject * >(impl) );
+}
+
+Runtime::~Runtime()
+{
+ Py_XDECREF( reinterpret_cast< PyObject * >(impl) );
+}
+
+Runtime & Runtime::operator = ( const Runtime & r )
+{
+ PyRef temp( reinterpret_cast< PyObject * >(r.impl) );
+ Py_XINCREF( temp.get() );
+ Py_XDECREF( reinterpret_cast< PyObject * >(impl) );
+ impl = r.impl;
+ return *this;
+}
+
+PyRef Runtime::any2PyObject (const Any &a ) const
+{
+ if( ! impl->cargo->valid )
+ {
+ throw RuntimeException("pyuno runtime must be initialized before calling any2PyObject" );
+ }
+
+ switch (a.getValueTypeClass ())
+ {
+ case css::uno::TypeClass_VOID:
+ {
+ Py_INCREF (Py_None);
+ return PyRef(Py_None);
+ }
+ case css::uno::TypeClass_CHAR:
+ {
+ sal_Unicode c = *o3tl::forceAccess<sal_Unicode>(a);
+ return PyRef( PyUNO_char_new( c , *this ), SAL_NO_ACQUIRE );
+ }
+ case css::uno::TypeClass_BOOLEAN:
+ {
+ bool b;
+ if ((a >>= b) && b)
+ return Py_True;
+ else
+ return Py_False;
+ }
+ case css::uno::TypeClass_BYTE:
+ case css::uno::TypeClass_SHORT:
+ case css::uno::TypeClass_UNSIGNED_SHORT:
+ case css::uno::TypeClass_LONG:
+ {
+ sal_Int32 l = 0;
+ a >>= l;
+ return PyRef( PyLong_FromLong (l), SAL_NO_ACQUIRE );
+ }
+ case css::uno::TypeClass_UNSIGNED_LONG:
+ {
+ sal_uInt32 l = 0;
+ a >>= l;
+ return PyRef( PyLong_FromUnsignedLong (l), SAL_NO_ACQUIRE );
+ }
+ case css::uno::TypeClass_HYPER:
+ {
+ sal_Int64 l = 0;
+ a >>= l;
+ return PyRef( PyLong_FromLongLong (l), SAL_NO_ACQUIRE);
+ }
+ case css::uno::TypeClass_UNSIGNED_HYPER:
+ {
+ sal_uInt64 l = 0;
+ a >>= l;
+ return PyRef( PyLong_FromUnsignedLongLong (l), SAL_NO_ACQUIRE);
+ }
+ case css::uno::TypeClass_FLOAT:
+ {
+ float f = 0.0;
+ a >>= f;
+ return PyRef(PyFloat_FromDouble (f), SAL_NO_ACQUIRE);
+ }
+ case css::uno::TypeClass_DOUBLE:
+ {
+ double d = 0.0;
+ a >>= d;
+ return PyRef( PyFloat_FromDouble (d), SAL_NO_ACQUIRE);
+ }
+ case css::uno::TypeClass_STRING:
+ {
+ OUString tmp_ostr;
+ a >>= tmp_ostr;
+ return ustring2PyUnicode( tmp_ostr );
+ }
+ case css::uno::TypeClass_TYPE:
+ {
+ Type t;
+ a >>= t;
+ OString o = OUStringToOString( t.getTypeName(), RTL_TEXTENCODING_ASCII_US );
+ return PyRef(
+ PyUNO_Type_new (
+ o.getStr(), t.getTypeClass(), *this),
+ SAL_NO_ACQUIRE);
+ }
+ case css::uno::TypeClass_ANY:
+ {
+ //I don't think this can happen.
+ Py_INCREF (Py_None);
+ return Py_None;
+ }
+ case css::uno::TypeClass_ENUM:
+ {
+ sal_Int32 l = *static_cast<sal_Int32 const *>(a.getValue());
+ TypeDescription desc( a.getValueType() );
+ if( desc.is() )
+ {
+ desc.makeComplete();
+ typelib_EnumTypeDescription *pEnumDesc =
+ reinterpret_cast<typelib_EnumTypeDescription *>(desc.get());
+ for( int i = 0 ; i < pEnumDesc->nEnumValues ; i ++ )
+ {
+ if( pEnumDesc->pEnumValues[i] == l )
+ {
+ OString v = OUStringToOString( OUString::unacquired(&pEnumDesc->ppEnumNames[i]), RTL_TEXTENCODING_ASCII_US);
+ OString e = OUStringToOString( OUString::unacquired(&pEnumDesc->aBase.pTypeName), RTL_TEXTENCODING_ASCII_US);
+ return PyRef( PyUNO_Enum_new(e.getStr(),v.getStr(), *this ), SAL_NO_ACQUIRE );
+ }
+ }
+ }
+ throw RuntimeException( "Any carries enum " + a.getValueType().getTypeName() +
+ " with invalid value " + OUString::number(l) );
+ }
+ case css::uno::TypeClass_EXCEPTION:
+ case css::uno::TypeClass_STRUCT:
+ {
+ PyRef excClass = getClass( a.getValueType().getTypeName(), *this );
+ PyRef value = PyUNOStruct_new( a, getImpl()->cargo->xInvocation );
+ PyRef argsTuple( PyTuple_New( 1 ) , SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem( argsTuple.get() , 0 , value.getAcquired() );
+ PyRef ret( PyObject_CallObject( excClass.get() , argsTuple.get() ), SAL_NO_ACQUIRE );
+ if( ! ret.is() )
+ {
+ throw RuntimeException( "Couldn't instantiate python representation of structured UNO type " +
+ a.getValueType().getTypeName() );
+ }
+
+ if( auto e = o3tl::tryAccess<css::uno::Exception>(a) )
+ {
+ // add the message in a standard python way !
+ PyRef args( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
+
+ PyRef pymsg = ustring2PyString( e->Message );
+ PyTuple_SetItem( args.get(), 0 , pymsg.getAcquired() );
+ // the exception base functions want to have an "args" tuple,
+ // which contains the message
+ PyObject_SetAttrString( ret.get(), "args", args.get() );
+ }
+ return ret;
+ }
+ case css::uno::TypeClass_SEQUENCE:
+ {
+ Sequence<Any> s;
+
+ Sequence< sal_Int8 > byteSequence;
+ if( a >>= byteSequence )
+ {
+ // byte sequence is treated in a special way because of performance reasons
+ // @since 0.9.2
+ return PyRef( PyUNO_ByteSequence_new( byteSequence, *this ), SAL_NO_ACQUIRE );
+ }
+ else
+ {
+ Reference< XTypeConverter > tc = getImpl()->cargo->xTypeConverter;
+ tc->convertTo (a, cppu::UnoType<decltype(s)>::get()) >>= s;
+ PyRef tuple( PyTuple_New (s.getLength()), SAL_NO_ACQUIRE, NOT_NULL);
+ int i=0;
+ try
+ {
+ for ( i = 0; i < s.getLength (); i++)
+ {
+ PyRef element = any2PyObject (tc->convertTo (s[i], s[i].getValueType() ));
+ OSL_ASSERT( element.is() );
+ PyTuple_SetItem( tuple.get(), i, element.getAcquired() );
+ }
+ }
+ catch( css::uno::Exception & )
+ {
+ for( ; i < s.getLength() ; i ++ )
+ {
+ Py_INCREF( Py_None );
+ PyTuple_SetItem( tuple.get(), i, Py_None );
+ }
+ throw;
+ }
+ return tuple;
+ }
+ }
+ case css::uno::TypeClass_INTERFACE:
+ {
+ Reference<XInterface> tmp_interface;
+ a >>= tmp_interface;
+ if (!tmp_interface.is ())
+ return Py_None;
+
+ return PyUNO_new( a, getImpl()->cargo->xInvocation );
+ }
+ default:
+ {
+ throw RuntimeException( "Unknown UNO type class " + OUString::number(static_cast<int>(a.getValueTypeClass())) );
+ }
+ }
+}
+
+static Sequence< Type > invokeGetTypes( const Runtime & r , PyObject * o )
+{
+ Sequence< Type > ret;
+
+ PyRef method( PyObject_GetAttrString( o , "getTypes" ), SAL_NO_ACQUIRE );
+ raiseInvocationTargetExceptionWhenNeeded( r );
+ if( method.is() && PyCallable_Check( method.get() ) )
+ {
+ PyRef types( PyObject_CallObject( method.get(), nullptr ) , SAL_NO_ACQUIRE );
+ raiseInvocationTargetExceptionWhenNeeded( r );
+ if( types.is() && PyTuple_Check( types.get() ) )
+ {
+ int size = PyTuple_Size( types.get() );
+
+ // add the XUnoTunnel interface for uno object identity concept (hack)
+ ret.realloc( size + 1 );
+ auto pret = ret.getArray();
+ for( int i = 0 ; i < size ; i ++ )
+ {
+ Any a = r.pyObject2Any(PyTuple_GetItem(types.get(),i));
+ a >>= pret[i];
+ }
+ pret[size] = cppu::UnoType<css::lang::XUnoTunnel>::get();
+ }
+ }
+ return ret;
+}
+
+static OUString
+lcl_ExceptionMessage(PyObject *const o, OUString const*const pWrapped)
+{
+ OUStringBuffer buf;
+ buf.append("Couldn't convert ");
+ PyRef reprString( PyObject_Str(o), SAL_NO_ACQUIRE );
+ buf.appendAscii( PyUnicode_AsUTF8(reprString.get()) );
+ buf.append(" to a UNO type");
+ if (pWrapped)
+ {
+ buf.append("; caught exception: ");
+ buf.append(*pWrapped);
+ }
+ return buf.makeStringAndClear();
+}
+
+// For Python 2.7 - see https://bugs.python.org/issue24161
+// Fills aSeq and returns true if pObj is a valid iterator
+bool Runtime::pyIterUnpack( PyObject *const pObj, Any &a ) const
+{
+ if( !PyIter_Check( pObj ))
+ return false;
+
+ PyObject *pItem = PyIter_Next( pObj );
+ if( !pItem )
+ {
+ if( PyErr_Occurred() )
+ {
+ PyErr_Clear();
+ return false;
+ }
+ return true;
+ }
+
+ ::std::vector<Any> items;
+ do
+ {
+ PyRef rItem( pItem, SAL_NO_ACQUIRE );
+ items.push_back( pyObject2Any( rItem.get() ) );
+ pItem = PyIter_Next( pObj );
+ }
+ while( pItem );
+ a <<= comphelper::containerToSequence(items);
+ return true;
+}
+
+Any Runtime::pyObject2Any(const PyRef & source, enum ConversionMode mode) const
+{
+ if (!impl || !impl->cargo->valid)
+ {
+ throw RuntimeException("pyuno runtime must be initialized before calling any2PyObject" );
+ }
+
+ Any a;
+ PyObject *o = source.get();
+ if( Py_None == o )
+ {
+
+ }
+ else if (PyLong_Check (o))
+ {
+ // Convert the Python 3 booleans that are actually of type PyLong.
+ if(o == Py_True)
+ {
+ a <<= true;
+ }
+ else if(o == Py_False)
+ {
+ a <<= false;
+ }
+ else
+ {
+ sal_Int64 l = static_cast<sal_Int64>(PyLong_AsLong (o));
+ if( l < 128 && l >= -128 )
+ {
+ sal_Int8 b = static_cast<sal_Int8>(l);
+ a <<= b;
+ }
+ else if( l <= 0x7fff && l >= -0x8000 )
+ {
+ sal_Int16 s = static_cast<sal_Int16>(l);
+ a <<= s;
+ }
+ else if( l <= SAL_CONST_INT64(0x7fffffff) &&
+ l >= -SAL_CONST_INT64(0x80000000) )
+ {
+ sal_Int32 l32 = static_cast<sal_Int32>(l);
+ a <<= l32;
+ }
+ else
+ {
+ a <<= l;
+ }
+ }
+ }
+ else if (PyFloat_Check (o))
+ {
+ double d = PyFloat_AsDouble (o);
+ a <<= d;
+ }
+ else if (PyBytes_Check(o) || PyUnicode_Check(o))
+ {
+ a <<= pyString2ustring(o);
+ }
+ else if (PyTuple_Check (o))
+ {
+ Sequence<Any> s (PyTuple_Size (o));
+ auto sRange = asNonConstRange(s);
+ for (Py_ssize_t i = 0; i < PyTuple_Size (o); i++)
+ {
+ sRange[i] = pyObject2Any (PyTuple_GetItem (o, i), mode );
+ }
+ a <<= s;
+ }
+ else if (PyList_Check (o))
+ {
+ Py_ssize_t l = PyList_Size (o);
+ Sequence<Any> s (l);
+ auto sRange = asNonConstRange(s);
+ for (Py_ssize_t i = 0; i < l; i++)
+ {
+ sRange[i] = pyObject2Any (PyList_GetItem (o, i), mode );
+ }
+ a <<= s;
+ }
+ else if (!pyIterUnpack (o, a))
+ {
+ Runtime runtime;
+ // should be removed, in case ByteSequence gets derived from String
+ if( PyObject_IsInstance( o, getByteSequenceClass( runtime ).get() ) )
+ {
+ PyRef str(PyObject_GetAttrString( o , "value" ),SAL_NO_ACQUIRE);
+ Sequence< sal_Int8 > seq;
+ if( PyBytes_Check( str.get() ) )
+ {
+ seq = Sequence<sal_Int8 > (
+ reinterpret_cast<sal_Int8*>(PyBytes_AsString(str.get())), PyBytes_Size(str.get()));
+ }
+ a <<= seq;
+ }
+ else
+ if( PyObject_IsInstance( o, getTypeClass( runtime ).get() ) )
+ {
+ Type t = PyType2Type( o );
+ a <<= t;
+ }
+ else if( PyObject_IsInstance( o, getEnumClass( runtime ).get() ) )
+ {
+ a = PyEnum2Enum( o );
+ }
+ else if( isInstanceOfStructOrException( o ) )
+ {
+ PyRef struc(PyObject_GetAttrString( o , "value" ),SAL_NO_ACQUIRE);
+ PyUNO * obj = reinterpret_cast<PyUNO*>(struc.get());
+ Reference< XMaterialHolder > holder( obj->members->xInvocation, UNO_QUERY );
+ if( !holder.is( ) )
+ {
+ throw RuntimeException(
+ "struct or exception wrapper does not support XMaterialHolder" );
+ }
+
+ a = holder->getMaterial();
+
+ }
+ else if( PyObject_IsInstance( o, getPyUnoClass().get() ) )
+ {
+ PyUNO* o_pi = reinterpret_cast<PyUNO*>(o);
+ a = o_pi->members->wrappedObject;
+ }
+ else if( PyObject_IsInstance( o, getPyUnoStructClass().get() ) )
+ {
+ PyUNO* o_pi = reinterpret_cast<PyUNO*>(o);
+ Reference<XMaterialHolder> my_mh (o_pi->members->xInvocation, css::uno::UNO_QUERY_THROW);
+ a = my_mh->getMaterial();
+ }
+ else if( PyObject_IsInstance( o, getCharClass( runtime ).get() ) )
+ {
+ a <<= PyChar2Unicode( o );
+ }
+ else if( PyObject_IsInstance( o, getAnyClass( runtime ).get() ) )
+ {
+ if( ACCEPT_UNO_ANY != mode )
+ {
+ throw RuntimeException(
+ "uno.Any instance not accepted during method call, "
+ "use uno.invoke instead" );
+ }
+
+ a = pyObject2Any( PyRef( PyObject_GetAttrString( o , "value" ), SAL_NO_ACQUIRE) );
+ Type t;
+ pyObject2Any( PyRef( PyObject_GetAttrString( o, "type" ), SAL_NO_ACQUIRE ) ) >>= t;
+
+ try
+ {
+ a = getImpl()->cargo->xTypeConverter->convertTo( a, t );
+ }
+ catch( const css::uno::Exception & e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetRuntimeException(
+ e.Message, e.Context, anyEx);
+ }
+
+ }
+ else
+ {
+ Reference< XInterface > mappedObject;
+ Reference< XInvocation > adapterObject;
+
+ // instance already mapped out to the world ?
+ PyRef2Adapter::iterator ii = impl->cargo->mappedObjects.find( PyRef( o ) );
+ if( ii != impl->cargo->mappedObjects.end() )
+ {
+ adapterObject = ii->second;
+ }
+
+ if( adapterObject.is() )
+ {
+ // object got already bridged !
+ auto pAdapter = comphelper::getFromUnoTunnel<Adapter>(adapterObject);
+
+ mappedObject = impl->cargo->xAdapterFactory->createAdapter(
+ adapterObject, pAdapter->getWrappedTypes() );
+ }
+ else
+ {
+ try {
+ Sequence<Type> interfaces = invokeGetTypes(*this, o);
+ if (interfaces.getLength())
+ {
+ rtl::Reference<Adapter> pAdapter = new Adapter( o, interfaces );
+ mappedObject =
+ getImpl()->cargo->xAdapterFactory->createAdapter(
+ pAdapter, interfaces );
+
+ // keep a list of exported objects to ensure object identity !
+ impl->cargo->mappedObjects[ PyRef(o) ] =
+ css::uno::WeakReference< XInvocation > ( pAdapter );
+ }
+ } catch (InvocationTargetException const& e) {
+ OUString const msg(lcl_ExceptionMessage(o, &e.Message));
+ throw WrappedTargetRuntimeException( // re-wrap that
+ msg, e.Context, e.TargetException);
+ }
+ }
+ if( mappedObject.is() )
+ {
+ a <<= mappedObject;
+ }
+ else
+ {
+ OUString const msg(lcl_ExceptionMessage(o, nullptr));
+ throw RuntimeException(msg);
+ }
+ }
+ }
+ return a;
+}
+
+Any Runtime::extractUnoException( const PyRef & excType, const PyRef &excValue, const PyRef &excTraceback) const
+{
+ OUString str;
+ Any ret;
+ if( excTraceback.is() )
+ {
+ Exception e;
+ PyRef unoModule;
+ if ( impl )
+ {
+ try
+ {
+ unoModule = impl->cargo->getUnoModule();
+ }
+ catch (const Exception &ei)
+ {
+ e=ei;
+ }
+ }
+ if( unoModule.is() )
+ {
+ PyRef extractTraceback(
+ PyDict_GetItemString(unoModule.get(),"_uno_extract_printable_stacktrace" ) );
+
+ if( PyCallable_Check(extractTraceback.get()) )
+ {
+ PyRef args( PyTuple_New( 1), SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem( args.get(), 0, excTraceback.getAcquired() );
+ PyRef pyStr( PyObject_CallObject( extractTraceback.get(),args.get() ), SAL_NO_ACQUIRE);
+ str = OUString::fromUtf8(PyUnicode_AsUTF8(pyStr.get()));
+ }
+ else
+ {
+ str = "Couldn't find uno._uno_extract_printable_stacktrace";
+ }
+ }
+ else
+ {
+ str = "Could not load uno.py, no stacktrace available";
+ if ( !e.Message.isEmpty() )
+ {
+ str += " (Error loading uno.py: " + e.Message + ")";
+ }
+ }
+
+ }
+ else
+ {
+ // it may occur, that no traceback is given (e.g. only native code below)
+ str = "no traceback available";
+ }
+
+ if( isInstanceOfStructOrException( excValue.get() ) )
+ {
+ ret = pyObject2Any( excValue );
+ }
+ else
+ {
+ OUStringBuffer buf;
+ PyRef typeName( PyObject_Str( excType.get() ), SAL_NO_ACQUIRE );
+ if( typeName.is() )
+ {
+ buf.appendAscii( PyUnicode_AsUTF8( typeName.get() ) );
+ }
+ else
+ {
+ buf.append( "no typename available" );
+ }
+ buf.append( ": " );
+ PyRef valueRep( PyObject_Str( excValue.get() ), SAL_NO_ACQUIRE );
+ if( valueRep.is() )
+ {
+ buf.appendAscii( PyUnicode_AsUTF8( valueRep.get()));
+ }
+ else
+ {
+ buf.append( "Couldn't convert exception value to a string" );
+ }
+ buf.append( ", traceback follows\n" );
+ if( !str.isEmpty() )
+ {
+ buf.append( str );
+ buf.append( "\n" );
+ }
+ else
+ {
+ buf.append( ", no traceback available\n" );
+ }
+ RuntimeException e(buf.makeStringAndClear());
+ SAL_WARN("pyuno.runtime", "Python exception: " << e.Message);
+ ret <<= e;
+ }
+ return ret;
+}
+
+
+PyThreadAttach::PyThreadAttach( PyInterpreterState *interp)
+ : m_isNewState(false)
+{
+ // note: *may* be called recursively, with PyThreadDetach between - in
+ // that case, don't create *new* PyThreadState but reuse!
+ tstate = PyGILState_GetThisThreadState(); // from TLS, possibly detached
+ if (!tstate)
+ {
+ m_isNewState = true;
+ tstate = PyThreadState_New( interp );
+ }
+ if( !tstate )
+ throw RuntimeException( "Couldn't create a pythreadstate" );
+ PyEval_AcquireThread( tstate);
+}
+
+PyThreadAttach::~PyThreadAttach()
+{
+ if (m_isNewState)
+ { // Clear needs GIL!
+ PyThreadState_Clear( tstate );
+ // note: PyThreadState_Delete(tstate) cannot be called, it will assert
+ // because it requires a PyThreadState to be set, but not the tstate!
+ PyThreadState_DeleteCurrent();
+ }
+ else
+ {
+ PyEval_ReleaseThread( tstate );
+ }
+}
+
+PyThreadDetach::PyThreadDetach()
+{
+ tstate = PyThreadState_Get();
+ PyEval_ReleaseThread( tstate );
+ // tstate must not be deleted here! lots of pointers to it on the stack
+}
+
+ /** Acquires the global interpreter lock again
+
+ */
+PyThreadDetach::~PyThreadDetach()
+{
+ PyEval_AcquireThread( tstate );
+}
+
+
+PyRef const & RuntimeCargo::getUnoModule()
+{
+ if( ! dictUnoModule.is() )
+ {
+ dictUnoModule = importUnoModule();
+ }
+ return dictUnoModule;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_struct.cxx b/pyuno/source/module/pyuno_struct.cxx
new file mode 100644
index 0000000000..c8fd7e9879
--- /dev/null
+++ b/pyuno/source/module/pyuno_struct.cxx
@@ -0,0 +1,397 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <com/sun/star/script/CannotConvertException.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include "pyuno_impl.hxx"
+
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::UNO_QUERY;
+using com::sun::star::uno::TypeClass;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Exception;
+using com::sun::star::lang::XSingleServiceFactory;
+using com::sun::star::script::XInvocation2;
+using com::sun::star::beans::XMaterialHolder;
+
+namespace pyuno
+{
+
+static void PyUNOStruct_del( PyObject* self )
+{
+ PyUNO *me = reinterpret_cast<PyUNO*>( self );
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del( self );
+}
+
+static PyObject *PyUNOStruct_str( PyObject *self )
+{
+ PyUNO *me = reinterpret_cast<PyUNO*>( self );
+ OString buf;
+
+ Reference<XMaterialHolder> rHolder( me->members->xInvocation,UNO_QUERY );
+ if( rHolder.is() )
+ {
+ PyThreadDetach antiguard;
+ Any a = rHolder->getMaterial();
+ OUString s = val2str( a.getValue(), a.getValueType().getTypeLibType() );
+ buf = OUStringToOString( s, RTL_TEXTENCODING_ASCII_US );
+ }
+
+ return PyUnicode_FromString( buf.getStr());
+}
+
+static PyObject *PyUNOStruct_repr( PyObject *self )
+{
+ PyUNO *me = reinterpret_cast<PyUNO*>( self );
+ PyObject *ret = nullptr;
+
+ if( me->members->wrappedObject.getValueType().getTypeClass()
+ == css::uno::TypeClass_EXCEPTION )
+ {
+ Reference< XMaterialHolder > rHolder(me->members->xInvocation,UNO_QUERY);
+ if( rHolder.is() )
+ {
+ Any a = rHolder->getMaterial();
+ Exception e;
+ a >>= e;
+ ret = ustring2PyUnicode(e.Message ).getAcquired();
+ }
+ }
+ else
+ {
+ ret = PyUNOStruct_str( self );
+ }
+
+ return ret;
+}
+
+static PyObject* PyUNOStruct_dir( PyObject *self )
+{
+ PyUNO *me = reinterpret_cast<PyUNO*>( self );
+
+ PyObject* member_list = nullptr;
+
+ try
+ {
+ member_list = PyList_New( 0 );
+ const css::uno::Sequence<OUString> aMemberNames = me->members->xInvocation->getMemberNames();
+ for( const auto& aMember : aMemberNames )
+ {
+ // setitem steals a reference
+ PyList_Append( member_list, ustring2PyString( aMember ).getAcquired() );
+ }
+ }
+ catch( const RuntimeException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+
+ return member_list;
+}
+
+static PyObject* PyUNOStruct_getattr( PyObject* self, char* name )
+{
+ PyUNO *me = reinterpret_cast<PyUNO*>( self );
+
+ try
+ {
+ Runtime runtime;
+
+ me = reinterpret_cast<PyUNO*>(self);
+ if (strcmp (name, "__dict__") == 0)
+ {
+ Py_INCREF (Py_TYPE(me)->tp_dict);
+ return Py_TYPE(me)->tp_dict;
+ }
+ if( strcmp( name, "__class__" ) == 0 )
+ {
+ return getClass(
+ me->members->wrappedObject.getValueType().getTypeName(), runtime ).getAcquired();
+ }
+
+ PyObject *pRet = PyObject_GenericGetAttr( self, PyUnicode_FromString( name ) );
+ if( pRet )
+ return pRet;
+ PyErr_Clear();
+
+ OUString attrName( OUString::createFromAscii( name ) );
+ if( me->members->xInvocation->hasProperty( attrName ) )
+ {
+ //Return the value of the property
+ Any anyRet;
+ {
+ PyThreadDetach antiguard;
+ anyRet = me->members->xInvocation->getValue( attrName );
+ }
+ PyRef ret = runtime.any2PyObject( anyRet );
+ Py_XINCREF( ret.get() );
+ return ret.get();
+ }
+
+ //or else...
+ PyErr_SetString (PyExc_AttributeError, name);
+ }
+ catch( const css::reflection::InvocationTargetException & e )
+ {
+ raisePyExceptionWithAny( e.TargetException );
+ }
+ catch( const css::beans::UnknownPropertyException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const css::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+ catch( const RuntimeException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ }
+
+ return nullptr;
+}
+
+static int PyUNOStruct_setattr (PyObject* self, char* name, PyObject* value)
+{
+ PyUNO* me;
+
+ me = reinterpret_cast<PyUNO*>(self);
+ try
+ {
+ Runtime runtime;
+ Any val= runtime.pyObject2Any(value, ACCEPT_UNO_ANY);
+
+ OUString attrName( OUString::createFromAscii( name ) );
+ {
+ PyThreadDetach antiguard;
+ if (me->members->xInvocation->hasProperty (attrName))
+ {
+ me->members->xInvocation->setValue (attrName, val);
+ return 0; //Keep with Python's boolean system
+ }
+ }
+ }
+ catch( const css::reflection::InvocationTargetException & e )
+ {
+ raisePyExceptionWithAny( e.TargetException );
+ return 1;
+ }
+ catch( const css::beans::UnknownPropertyException & e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ return 1;
+ }
+ catch( const css::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( Any(e) );
+ return 1;
+ }
+ catch( const RuntimeException & e )
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ return 1;
+ }
+ PyErr_SetString (PyExc_AttributeError, name);
+ return 1; //as above.
+}
+
+
+static PyObject* PyUNOStruct_cmp( PyObject *self, PyObject *that, int op )
+{
+ PyObject *result;
+
+ if(op != Py_EQ && op != Py_NE)
+ {
+ PyErr_SetString( PyExc_TypeError, "only '==' and '!=' comparisons are defined" );
+ return nullptr;
+ }
+ if( self == that )
+ {
+ result = (op == Py_EQ ? Py_True : Py_False);
+ Py_INCREF( result );
+ return result;
+ }
+ try
+ {
+ Runtime runtime;
+ if( PyObject_IsInstance( that, getPyUnoStructClass().get() ) )
+ {
+
+ PyUNO *me = reinterpret_cast< PyUNO * > ( self );
+ PyUNO *other = reinterpret_cast< PyUNO * > ( that );
+ css::uno::TypeClass tcMe = me->members->wrappedObject.getValueTypeClass();
+ css::uno::TypeClass tcOther = other->members->wrappedObject.getValueTypeClass();
+
+ if( tcMe == tcOther )
+ {
+ if( tcMe == css::uno::TypeClass_STRUCT ||
+ tcMe == css::uno::TypeClass_EXCEPTION )
+ {
+ Reference< XMaterialHolder > xMe( me->members->xInvocation,UNO_QUERY );
+ Reference< XMaterialHolder > xOther( other->members->xInvocation,UNO_QUERY );
+ if( xMe->getMaterial() == xOther->getMaterial() )
+ {
+ result = (op == Py_EQ ? Py_True : Py_False);
+ Py_INCREF( result );
+ return result;
+ }
+ }
+ }
+ }
+ }
+ catch( const css::uno::RuntimeException & e)
+ {
+ raisePyExceptionWithAny( Any( e ) );
+ }
+
+ result = (op == Py_EQ ? Py_False : Py_True);
+ Py_INCREF(result);
+ return result;
+}
+
+static PyMethodDef PyUNOStructMethods[] =
+{
+ {"__dir__", reinterpret_cast<PyCFunction>(PyUNOStruct_dir), METH_NOARGS, nullptr},
+ {nullptr, nullptr, 0, nullptr}
+};
+
+static PyTypeObject PyUNOStructType =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "pyuno.struct",
+ sizeof (PyUNO),
+ 0,
+ PyUNOStruct_del,
+#if PY_VERSION_HEX >= 0x03080000
+ 0, // Py_ssize_t tp_vectorcall_offset
+#else
+ nullptr, // printfunc tp_print
+#endif
+ PyUNOStruct_getattr,
+ PyUNOStruct_setattr,
+ /* this type does not exist in Python 3: (cmpfunc) */ nullptr,
+ PyUNOStruct_repr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ PyUNOStruct_str,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_RICHCOMPARE,
+ nullptr,
+ nullptr,
+ nullptr,
+ PyUNOStruct_cmp,
+ 0,
+ nullptr,
+ nullptr,
+ PyUNOStructMethods,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr
+ , 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#if PY_VERSION_HEX >= 0x03080000
+ , nullptr // vectorcallfunc tp_vectorcall
+#if PY_VERSION_HEX < 0x03090000
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+ , nullptr // tp_print
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+#endif
+#endif
+};
+
+int PyUNOStruct_initType()
+{
+ return PyType_Ready( &PyUNOStructType );
+}
+
+PyRef getPyUnoStructClass()
+{
+ return PyRef( reinterpret_cast< PyObject * > ( &PyUNOStructType ) );
+}
+
+PyRef PyUNOStruct_new (
+ const Any &targetInterface,
+ const Reference<XSingleServiceFactory> &ssf )
+{
+ Reference<XInvocation2> xInvocation;
+
+ {
+ PyThreadDetach antiguard;
+ xInvocation.set(
+ ssf->createInstanceWithArguments( Sequence<Any>( &targetInterface, 1 ) ), css::uno::UNO_QUERY_THROW );
+ }
+ if( !Py_IsInitialized() )
+ throw RuntimeException();
+
+ PyUNO* self = PyObject_New (PyUNO, &PyUNOStructType);
+ if (self == nullptr)
+ return PyRef(); // == error
+ self->members = new PyUNOInternals;
+ self->members->xInvocation = xInvocation;
+ self->members->wrappedObject = targetInterface;
+ return PyRef( reinterpret_cast<PyObject*>(self), SAL_NO_ACQUIRE );
+
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_type.cxx b/pyuno/source/module/pyuno_type.cxx
new file mode 100644
index 0000000000..a04b4e26d7
--- /dev/null
+++ b/pyuno/source/module/pyuno_type.cxx
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "pyuno_impl.hxx"
+
+#include <o3tl/any.hxx>
+
+#include <typelib/typedescription.hxx>
+
+
+using com::sun::star::uno::TypeClass;
+using com::sun::star::uno::Type;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::TypeDescription;
+
+namespace pyuno
+{
+const char *typeClassToString( TypeClass t )
+{
+ const char * ret = nullptr;
+ switch (t)
+ {
+ case css::uno::TypeClass_VOID:
+ ret = "VOID"; break;
+ case css::uno::TypeClass_CHAR:
+ ret = "CHAR"; break;
+ case css::uno::TypeClass_BOOLEAN:
+ ret = "BOOLEAN"; break;
+ case css::uno::TypeClass_BYTE:
+ ret = "BYTE"; break;
+ case css::uno::TypeClass_SHORT:
+ ret = "SHORT"; break;
+ case css::uno::TypeClass_UNSIGNED_SHORT:
+ ret = "UNSIGNED_SHORT"; break;
+ case css::uno::TypeClass_LONG:
+ ret = "LONG"; break;
+ case css::uno::TypeClass_UNSIGNED_LONG:
+ ret = "UNSIGNED_LONG"; break;
+ case css::uno::TypeClass_HYPER:
+ ret = "HYPER"; break;
+ case css::uno::TypeClass_UNSIGNED_HYPER:
+ ret = "UNSIGNED_HYPER"; break;
+ case css::uno::TypeClass_FLOAT:
+ ret = "FLOAT"; break;
+ case css::uno::TypeClass_DOUBLE:
+ ret = "DOUBLE"; break;
+ case css::uno::TypeClass_STRING:
+ ret = "STRING"; break;
+ case css::uno::TypeClass_TYPE:
+ ret = "TYPE"; break;
+ case css::uno::TypeClass_ANY:
+ ret = "ANY";break;
+ case css::uno::TypeClass_ENUM:
+ ret = "ENUM";break;
+ case css::uno::TypeClass_STRUCT:
+ ret = "STRUCT"; break;
+ case css::uno::TypeClass_EXCEPTION:
+ ret = "EXCEPTION"; break;
+ case css::uno::TypeClass_SEQUENCE:
+ ret = "SEQUENCE"; break;
+ case css::uno::TypeClass_INTERFACE:
+ ret = "INTERFACE"; break;
+ case css::uno::TypeClass_TYPEDEF:
+ ret = "TYPEDEF"; break;
+ case css::uno::TypeClass_SERVICE:
+ ret = "SERVICE"; break;
+ case css::uno::TypeClass_MODULE:
+ ret = "MODULE"; break;
+ case css::uno::TypeClass_INTERFACE_METHOD:
+ ret = "INTERFACE_METHOD"; break;
+ case css::uno::TypeClass_INTERFACE_ATTRIBUTE:
+ ret = "INTERFACE_ATTRIBUTE"; break;
+ default:
+ ret = "UNKNOWN"; break;
+ }
+ return ret;
+}
+
+static PyRef getClass( const Runtime & r , const char * name)
+{
+ return PyRef( PyDict_GetItemString( r.getImpl()->cargo->getUnoModule().get(), name ) );
+}
+
+PyRef getTypeClass( const Runtime & r )
+{
+ return getClass( r , "Type" );
+}
+
+PyRef getEnumClass( const Runtime & r )
+{
+ return getClass( r , "Enum" );
+}
+
+PyRef getCharClass( const Runtime & r )
+{
+ return getClass( r , "Char" );
+}
+
+PyRef getByteSequenceClass( const Runtime & r )
+{
+ return getClass( r , "ByteSequence" );
+}
+
+PyRef getAnyClass( const Runtime & r )
+{
+ return getClass( r , "Any" );
+}
+
+
+sal_Unicode PyChar2Unicode( PyObject *obj )
+{
+ PyRef value( PyObject_GetAttrString( obj, "value" ), SAL_NO_ACQUIRE );
+ if( ! PyUnicode_Check( value.get() ) )
+ {
+ throw RuntimeException(
+ "attribute value of uno.Char is not a unicode string" );
+ }
+
+ if( PyUnicode_GetLength( value.get() ) < 1 )
+ {
+ throw RuntimeException(
+ "uno.Char contains an empty unicode string");
+ }
+
+ sal_Unicode c = static_cast<sal_Unicode>(PyUnicode_ReadChar( value.get(), 0));
+ return c;
+}
+
+Any PyEnum2Enum( PyObject *obj )
+{
+ Any ret;
+ PyRef typeName( PyObject_GetAttrString( obj,"typeName" ), SAL_NO_ACQUIRE);
+ PyRef value( PyObject_GetAttrString( obj, "value" ), SAL_NO_ACQUIRE);
+ if( !PyUnicode_Check( typeName.get() ) || ! PyUnicode_Check( value.get() ) )
+ {
+ throw RuntimeException(
+ "attributes typeName and/or value of uno.Enum are not strings" );
+ }
+
+ OUString strTypeName( OUString::createFromAscii( PyUnicode_AsUTF8( typeName.get() ) ) );
+ char const *stringValue = PyUnicode_AsUTF8( value.get() );
+
+ TypeDescription desc( strTypeName );
+ if( !desc.is() )
+ {
+ throw RuntimeException( "enum " + OUString::createFromAscii( PyUnicode_AsUTF8(typeName.get()) ) + " is unknown" );
+ }
+
+ if(desc.get()->eTypeClass != typelib_TypeClass_ENUM )
+ {
+ throw RuntimeException( "pyuno.checkEnum: " + strTypeName + "is a " +
+ OUString::createFromAscii(typeClassToString( static_cast<css::uno::TypeClass>(desc.get()->eTypeClass))) +
+ ", expected ENUM" );
+ }
+
+ desc.makeComplete();
+
+ typelib_EnumTypeDescription *pEnumDesc = reinterpret_cast<typelib_EnumTypeDescription*>(desc.get());
+ int i = 0;
+ for( i = 0; i < pEnumDesc->nEnumValues ; i ++ )
+ {
+ if( OUString::unacquired(&pEnumDesc->ppEnumNames[i]).equalsAscii( stringValue ) )
+ {
+ break;
+ }
+ }
+ if( i == pEnumDesc->nEnumValues )
+ {
+ throw RuntimeException( "value " + OUString::createFromAscii( stringValue ) +
+ "is unknown in enum " +
+ OUString::createFromAscii( PyUnicode_AsUTF8( typeName.get() ) ) );
+ }
+ ret = Any( &pEnumDesc->pEnumValues[i], desc.get()->pWeakRef );
+
+ return ret;
+}
+
+
+Type PyType2Type( PyObject * o )
+{
+ PyRef pyName( PyObject_GetAttrString( o, "typeName" ), SAL_NO_ACQUIRE);
+ if( !PyUnicode_Check( pyName.get() ) )
+ {
+ throw RuntimeException(
+ "type object does not have typeName property" );
+ }
+
+ PyRef pyTC( PyObject_GetAttrString( o, "typeClass" ), SAL_NO_ACQUIRE );
+ Any enumValue = PyEnum2Enum( pyTC.get() );
+
+ OUString name( OUString::createFromAscii( PyUnicode_AsUTF8( pyName.get() ) ) );
+ TypeDescription desc( name );
+ if( ! desc.is() )
+ {
+ throw RuntimeException( "type " + name + " is unknown" );
+ }
+ css::uno::TypeClass tc = *o3tl::doAccess<css::uno::TypeClass>(enumValue);
+ if( static_cast<css::uno::TypeClass>(desc.get()->eTypeClass) != tc )
+ {
+ throw RuntimeException( "pyuno.checkType: " + name + " is a " +
+ OUString::createFromAscii( typeClassToString( static_cast<TypeClass>(desc.get()->eTypeClass)) ) +
+ ", but type got construct with typeclass " +
+ OUString::createFromAscii( typeClassToString( tc ) ) );
+ }
+ return desc.get()->pWeakRef;
+}
+
+static PyObject* callCtor( const Runtime &r , const char * clazz, const PyRef & args )
+{
+ PyRef code( PyDict_GetItemString( r.getImpl()->cargo->getUnoModule().get(), clazz ) );
+ if( ! code.is() )
+ {
+ OString buf = OString::Concat("couldn't access uno.") + clazz;
+ PyErr_SetString( PyExc_RuntimeError, buf.getStr() );
+ return nullptr;
+ }
+ PyRef instance( PyObject_CallObject( code.get(), args.get() ), SAL_NO_ACQUIRE);
+ Py_XINCREF( instance.get() );
+ return instance.get();
+
+}
+
+
+PyObject *PyUNO_Enum_new( const char *enumBase, const char *enumValue, const Runtime &r )
+{
+ PyRef args( PyTuple_New( 2 ), SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem( args.get() , 0 , PyUnicode_FromString( enumBase ) );
+ PyTuple_SetItem( args.get() , 1 , PyUnicode_FromString( enumValue ) );
+
+ return callCtor( r, "Enum" , args );
+}
+
+
+PyObject* PyUNO_Type_new (const char *typeName , TypeClass t , const Runtime &r )
+{
+ // retrieve type object
+ PyRef args(PyTuple_New( 2 ), SAL_NO_ACQUIRE, NOT_NULL);
+
+ PyTuple_SetItem( args.get() , 0 , PyUnicode_FromString( typeName ) );
+ PyObject *typeClass = PyUNO_Enum_new( "com.sun.star.uno.TypeClass" , typeClassToString(t), r );
+ if( ! typeClass )
+ return nullptr;
+ PyTuple_SetItem( args.get() , 1 , typeClass);
+
+ return callCtor( r, "Type" , args );
+}
+
+PyObject* PyUNO_char_new ( sal_Unicode val , const Runtime &r )
+{
+ // retrieve type object
+ PyRef args( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
+ static_assert(sizeof(sal_Unicode) == sizeof(Py_UCS2), "unexpected size");
+ PyTuple_SetItem( args.get() , 0 , PyUnicode_FromKindAndData( PyUnicode_2BYTE_KIND, &val ,1) );
+
+ return callCtor( r, "Char" , args );
+}
+
+PyObject *PyUNO_ByteSequence_new(
+ const css::uno::Sequence< sal_Int8 > &byteSequence, const Runtime &r )
+{
+ PyRef str(
+ PyBytes_FromStringAndSize( reinterpret_cast<char const *>(byteSequence.getConstArray()), byteSequence.getLength()),
+ SAL_NO_ACQUIRE );
+ PyRef args( PyTuple_New( 1 ), SAL_NO_ACQUIRE, NOT_NULL );
+ PyTuple_SetItem( args.get() , 0 , str.getAcquired() );
+ return callCtor( r, "ByteSequence" , args );
+
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_util.cxx b/pyuno/source/module/pyuno_util.cxx
new file mode 100644
index 0000000000..f01d33acfb
--- /dev/null
+++ b/pyuno/source/module/pyuno_util.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "pyuno_impl.hxx"
+
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/time.h>
+
+using com::sun::star::uno::Sequence;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::RuntimeException;
+
+namespace pyuno
+{
+PyRef ustring2PyUnicode( const OUString & str )
+{
+ PyRef ret;
+#if Py_UNICODE_SIZE == 2
+#ifdef MACOSX
+ ret = PyRef( PyUnicode_FromUnicode( reinterpret_cast<const unsigned short *>(str.getStr()), str.getLength() ), SAL_NO_ACQUIRE );
+#else
+ static_assert(sizeof (wchar_t) == Py_UNICODE_SIZE, "bad assumption");
+ ret = PyRef( PyUnicode_FromUnicode( reinterpret_cast<wchar_t const *>(str.getStr()), str.getLength() ), SAL_NO_ACQUIRE );
+#endif
+#else
+ OString sUtf8(OUStringToOString(str, RTL_TEXTENCODING_UTF8));
+ ret = PyRef( PyUnicode_DecodeUTF8( sUtf8.getStr(), sUtf8.getLength(), nullptr) , SAL_NO_ACQUIRE );
+#endif
+ return ret;
+}
+
+PyRef ustring2PyString( std::u16string_view str )
+{
+ OString o = OUStringToOString( str, osl_getThreadTextEncoding() );
+ return PyRef( PyUnicode_FromString( o.getStr() ), SAL_NO_ACQUIRE );
+}
+
+OUString pyString2ustring( PyObject *pystr )
+{
+ OUString ret;
+ if( PyUnicode_Check( pystr ) )
+ {
+#if Py_UNICODE_SIZE == 2
+ ret = OUString(
+ reinterpret_cast<sal_Unicode const *>(PyUnicode_AS_UNICODE( pystr )) );
+#else
+ Py_ssize_t size(0);
+ char const *pUtf8(PyUnicode_AsUTF8AndSize(pystr, &size));
+ ret = OUString(pUtf8, size, RTL_TEXTENCODING_UTF8);
+#endif
+ }
+ else
+ {
+ char *name = PyBytes_AsString(pystr); // hmmm... is this a good idea?
+ ret = OUString( name, strlen(name), osl_getThreadTextEncoding() );
+ }
+ return ret;
+}
+
+PyRef getObjectFromUnoModule( const Runtime &runtime, const char * func )
+{
+ PyRef object(PyDict_GetItemString( runtime.getImpl()->cargo->getUnoModule().get(), func ) );
+ if( !object.is() )
+ {
+ throw RuntimeException("couldn't find core function " + OUString::createFromAscii(func));
+ }
+ return object;
+}
+
+
+// Logging
+
+
+bool isLog( RuntimeCargo const * cargo, sal_Int32 loglevel )
+{
+ return cargo && cargo->logFile && loglevel <= cargo->logLevel;
+}
+
+void log( RuntimeCargo * cargo, sal_Int32 level, std::u16string_view logString )
+{
+ log( cargo, level, OUStringToOString( logString, osl_getThreadTextEncoding() ).getStr() );
+}
+
+void log( RuntimeCargo * cargo, sal_Int32 level, const char *str )
+{
+ if( !isLog( cargo, level ) )
+ return;
+
+ static const char *strLevel[] = { "NONE", "CALL", "ARGS" };
+
+ TimeValue systemTime;
+ TimeValue localTime;
+ oslDateTime localDateTime;
+
+ osl_getSystemTime( &systemTime );
+ osl_getLocalTimeFromSystemTime( &systemTime, &localTime );
+ osl_getDateTimeFromTimeValue( &localTime, &localDateTime );
+
+ fprintf( cargo->logFile,
+ "%4i-%02i-%02i %02i:%02i:%02i,%03lu [%s,tid %ld]: %s\n",
+ localDateTime.Year,
+ localDateTime.Month,
+ localDateTime.Day,
+ localDateTime.Hours,
+ localDateTime.Minutes,
+ localDateTime.Seconds,
+ sal::static_int_cast< unsigned long >(
+ localDateTime.NanoSeconds/1000000),
+ strLevel[level],
+ sal::static_int_cast< long >(
+ static_cast<sal_Int32>(osl::Thread::getCurrentIdentifier())),
+ str );
+}
+
+namespace {
+
+void appendPointer(OUStringBuffer & buffer, void * pointer) {
+ buffer.append(
+ sal::static_int_cast< sal_Int64 >(
+ reinterpret_cast< sal_IntPtr >(pointer)),
+ 16);
+}
+
+}
+
+void logException( RuntimeCargo *cargo, const char *intro,
+ void * ptr, std::u16string_view aFunctionName,
+ const void * data, const css::uno::Type & type )
+{
+ if( isLog( cargo, LogLevel::CALL ) )
+ {
+ OUStringBuffer buf( 128 );
+ buf.appendAscii( intro );
+ appendPointer(buf, ptr);
+ buf.append( OUString::Concat("].") + aFunctionName + " = " );
+ buf.append(
+ val2str( data, type.getTypeLibType(), VAL2STR_MODE_SHALLOW ) );
+ log( cargo,LogLevel::CALL, buf );
+ }
+
+}
+
+void logReply(
+ RuntimeCargo *cargo,
+ const char *intro,
+ void * ptr,
+ std::u16string_view aFunctionName,
+ const Any &returnValue,
+ const Sequence< Any > & aParams )
+{
+ OUStringBuffer buf( 128 );
+ buf.appendAscii( intro );
+ appendPointer(buf, ptr);
+ buf.append( OUString::Concat("].") + aFunctionName + "()=" );
+ if( isLog( cargo, LogLevel::ARGS ) )
+ {
+ buf.append(
+ val2str( returnValue.getValue(), returnValue.getValueTypeRef(), VAL2STR_MODE_SHALLOW) );
+ for( const auto & p : aParams )
+ {
+ buf.append( ", " + val2str( p.getValue(), p.getValueTypeRef(), VAL2STR_MODE_SHALLOW) );
+ }
+ }
+ log( cargo,LogLevel::CALL, buf );
+
+}
+
+void logCall( RuntimeCargo *cargo, const char *intro,
+ void * ptr, std::u16string_view aFunctionName,
+ const Sequence< Any > & aParams )
+{
+ OUStringBuffer buf( 128 );
+ buf.appendAscii( intro );
+ appendPointer(buf, ptr);
+ buf.append( OUString::Concat("].") + aFunctionName + "(" );
+ if( isLog( cargo, LogLevel::ARGS ) )
+ {
+ for( int i = 0; i < aParams.getLength() ; i ++ )
+ {
+ if( i > 0 )
+ buf.append( ", " );
+ buf.append(
+ val2str( aParams[i].getValue(), aParams[i].getValueTypeRef(), VAL2STR_MODE_SHALLOW) );
+ }
+ }
+ buf.append( ")" );
+ log( cargo,LogLevel::CALL, buf );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/uno.py b/pyuno/source/module/uno.py
new file mode 100644
index 0000000000..b2a75bd039
--- /dev/null
+++ b/pyuno/source/module/uno.py
@@ -0,0 +1,553 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+import pyuno
+import sys
+import traceback
+import warnings
+
+# since on Windows sal3.dll no longer calls WSAStartup
+import socket
+
+# All functions and variables starting with a underscore (_) must be
+# considered private and can be changed at any time. Don't use them.
+_component_context = pyuno.getComponentContext()
+
+
+def getComponentContext():
+ """Returns the UNO component context used to initialize the Python runtime."""
+
+ return _component_context
+
+
+def getCurrentContext():
+ """Returns the current context.
+
+ See http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context
+ for an explanation on the current context concept.
+ """
+
+ return pyuno.getCurrentContext()
+
+
+def setCurrentContext(newContext):
+ """Sets newContext as new UNO context.
+
+ The newContext must implement the XCurrentContext interface. The
+ implementation should handle the desired properties and delegate
+ unknown properties to the old context. Ensure that the old one
+ is reset when you leave your stack, see
+ http://udk.openoffice.org/common/man/concept/uno_contexts.html#current_context
+ """
+
+ return pyuno.setCurrentContext(newContext)
+
+
+def getConstantByName(constant):
+ """Looks up the value of an IDL constant by giving its explicit name."""
+
+ return pyuno.getConstantByName(constant)
+
+
+def getTypeByName(typeName):
+ """Returns a `uno.Type` instance of the type given by typeName.
+
+ If the type does not exist, a `com.sun.star.uno.RuntimeException` is raised.
+ """
+
+ return pyuno.getTypeByName(typeName)
+
+
+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) 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) 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, **kwargs)
+
+
+def getClass(typeName):
+ """Returns the class of a concrete UNO exception, struct, or interface."""
+
+ return pyuno.getClass(typeName)
+
+
+def isInterface(obj):
+ """Returns True, when obj is a class of a UNO interface."""
+
+ return pyuno.isInterface(obj)
+
+
+def generateUuid():
+ """Returns a 16 byte sequence containing a newly generated uuid or guid.
+
+ For more information, see rtl/uuid.h.
+ """
+
+ return pyuno.generateUuid()
+
+
+def systemPathToFileUrl(systemPath):
+ """Returns a file URL for the given system path."""
+
+ return pyuno.systemPathToFileUrl(systemPath)
+
+
+def fileUrlToSystemPath(url):
+ """Returns a system path.
+
+ This path is determined by the system that the Python interpreter is running on.
+ """
+
+ return pyuno.fileUrlToSystemPath(url)
+
+
+def absolutize(path, relativeUrl):
+ """Returns an absolute file url from the given urls."""
+
+ return pyuno.absolutize(path, relativeUrl)
+
+
+class Enum:
+ """Represents a UNO enum.
+
+ Use an instance of this class to explicitly pass an enum to UNO.
+
+ :param typeName: The name of the enum as a string.
+ :param value: The actual value of this enum as a string.
+ """
+
+ def __init__(self, typeName, value):
+ self.typeName = typeName
+ self.value = value
+ pyuno.checkEnum(self)
+
+ def __repr__(self):
+ return "<Enum instance %s (%r)>" % (self.typeName, self.value)
+
+ def __eq__(self, that):
+ if not isinstance(that, Enum):
+ return False
+
+ return (self.typeName == that.typeName) and (self.value == that.value)
+
+ def __ne__(self,other):
+ return not self.__eq__(other)
+
+
+class Type:
+ """Represents a UNO type.
+
+ Use an instance of this class to explicitly pass a type to UNO.
+
+ :param typeName: Name of the UNO type
+ :param typeClass: Python Enum of TypeClass, see com/sun/star/uno/TypeClass.idl
+ """
+
+ def __init__(self, typeName, typeClass):
+ self.typeName = typeName
+ self.typeClass = typeClass
+ pyuno.checkType(self)
+
+ def __repr__(self):
+ return "<Type instance %s (%r)>" % (self.typeName, self.typeClass)
+
+ def __eq__(self, that):
+ if not isinstance(that, Type):
+ return False
+
+ return self.typeClass == that.typeClass and self.typeName == that.typeName
+
+ def __ne__(self,other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return self.typeName.__hash__()
+
+
+class Bool(object):
+ """Represents a UNO boolean.
+
+ Use an instance of this class to explicitly pass a boolean to UNO.
+
+ Note: This class is deprecated. Use Python's True and False directly instead.
+ """
+
+ def __new__(cls, value):
+ message = "The Bool class is deprecated. Use Python's True and False directly instead."
+ warnings.warn(message, DeprecationWarning)
+
+ if isinstance(value, str) and value == "true":
+ return True
+
+ if isinstance(value, str) and value == "false":
+ return False
+
+ if value:
+ return True
+
+ return False
+
+
+class Char:
+ """Represents a UNO char.
+
+ Use an instance of this class to explicitly pass a char to UNO.
+
+ For Python 3, this class only works with unicode (str) objects. Creating
+ a Char instance with a bytes object or comparing a Char instance
+ to a bytes object will raise an AssertionError.
+
+ :param value: A Unicode string with length 1
+ """
+
+ def __init__(self, value):
+ assert isinstance(value, str), "Expected str object, got %s instead." % type(value)
+
+ assert len(value) == 1, "Char value must have length of 1."
+ assert ord(value[0]) <= 0xFFFF, "Char value must be UTF-16 code unit"
+
+ self.value = value
+
+ def __repr__(self):
+ return "<Char instance %s>" % (self.value,)
+
+ def __eq__(self, that):
+ if isinstance(that, str):
+ if len(that) > 1:
+ return False
+
+ return self.value == that[0]
+
+ if isinstance(that, Char):
+ return self.value == that.value
+
+ return False
+
+ def __ne__(self,other):
+ return not self.__eq__(other)
+
+
+class ByteSequence:
+ """Represents a UNO ByteSequence value.
+
+ Use an instance of this class to explicitly pass a byte sequence to UNO.
+
+ :param value: A string or bytesequence
+ """
+
+ def __init__(self, value):
+ if isinstance(value, bytes):
+ self.value = value
+
+ elif isinstance(value, ByteSequence):
+ self.value = value.value
+
+ else:
+ raise TypeError("Expected bytes object or ByteSequence, got %s instead." % type(value))
+
+ def __repr__(self):
+ return "<ByteSequence instance '%s'>" % (self.value,)
+
+ def __eq__(self, that):
+ if isinstance(that, bytes):
+ return self.value == that
+
+ if isinstance(that, ByteSequence):
+ return self.value == that.value
+
+ return False
+
+ def __len__(self):
+ return len(self.value)
+
+ def __getitem__(self, index):
+ return self.value[index]
+
+ def __iter__(self):
+ return self.value.__iter__()
+
+ def __add__(self, b):
+ if isinstance(b, bytes):
+ return ByteSequence(self.value + b)
+
+ elif isinstance(b, ByteSequence):
+ return ByteSequence(self.value + b.value)
+
+ else:
+ raise TypeError("Can't add ByteString and %s." % type(b))
+
+ def __hash__(self):
+ return self.value.hash()
+
+
+class Any:
+ """Represents a UNO Any value.
+
+ Use only in connection with uno.invoke() to pass an explicit typed Any.
+ """
+
+ def __init__(self, type, value):
+ if isinstance(type, Type):
+ self.type = type
+ else:
+ self.type = getTypeByName(type)
+
+ self.value = value
+
+
+def invoke(object, methodname, argTuple):
+ """Use this function to pass exactly typed Anys to the callee (using uno.Any)."""
+
+ return pyuno.invoke(object, methodname, argTuple)
+
+
+# -----------------------------------------------------------------------------
+# Don't use any functions beyond this point; private section, likely to change.
+# -----------------------------------------------------------------------------
+_builtin_import = __import__
+
+
+def _uno_import(name, *optargs, **kwargs):
+ """Overrides built-in import to allow directly importing LibreOffice classes."""
+
+ try:
+ return _builtin_import(name, *optargs, **kwargs)
+ except ImportError as e:
+ # process optargs
+ globals, locals, fromlist = list(optargs)[:3] + [kwargs.get('globals', {}), kwargs.get('locals', {}),
+ kwargs.get('fromlist', [])][len(optargs):]
+
+ # from import form only, but skip if a uno lookup has already failed
+ if not fromlist or hasattr(e, '_uno_import_failed'):
+ raise
+
+ # hang onto exception for possible use on subsequent uno lookup failure
+ py_import_exc = e
+
+ mod = None
+ d = sys.modules
+
+ for module in name.split("."):
+ if module in d:
+ mod = d[module]
+ else:
+ # How to create a module ??
+ mod = pyuno.__class__(module)
+ if mod is None:
+ raise py_import_exc
+
+ d = mod.__dict__
+
+ RuntimeException = pyuno.getClass("com.sun.star.uno.RuntimeException")
+
+ for class_name in fromlist:
+ if class_name not in d:
+ failed = False
+
+ try:
+ # check for structs, exceptions or interfaces
+ d[class_name] = pyuno.getClass(name + "." + class_name)
+ except RuntimeException:
+ # check for enums
+ try:
+ d[class_name] = Enum(name, class_name)
+ except RuntimeException:
+ # check for constants
+ try:
+ d[class_name] = getConstantByName(name + "." + class_name)
+ except RuntimeException:
+ # check for constant group
+ try:
+ d[class_name] = _impl_getConstantGroupByName(name, class_name)
+ except ValueError:
+ failed = True
+
+ if failed:
+ # We have an import failure, but cannot distinguish between
+ # uno and non-uno errors as uno lookups are attempted for all
+ # "from xxx import yyy" imports following a python failure.
+ #
+ # In Python 3, the original python exception traceback is reused
+ # to help pinpoint the actual failing location. Its original
+ # message, unlike Python 2, is unlikely to be helpful for uno
+ # failures, as it most commonly is just a top level module like
+ # 'com'. So our exception appends the uno lookup failure.
+ # This is more ambiguous, but it plus the traceback should be
+ # sufficient to identify a root cause for python or uno issues.
+ #
+ # Our exception is raised outside of the nested exception
+ # handlers above, to avoid Python 3 nested exception
+ # information for the RuntimeExceptions during lookups.
+ #
+ # Finally, a private attribute is used to prevent further
+ # processing if this failure was in a nested import. That
+ # keeps the exception relevant to the primary failure point,
+ # preventing us from re-processing our own import errors.
+
+ uno_import_exc = ImportError("%s (or '%s.%s' is unknown)" %
+ (py_import_exc, name, class_name))
+
+ uno_import_exc = uno_import_exc.with_traceback(py_import_exc.__traceback__)
+
+ uno_import_exc._uno_import_failed = True
+ raise uno_import_exc
+
+ return mod
+
+
+try:
+ import __builtin__
+except ImportError:
+ import builtins as __builtin__
+
+# hook into the __import__ chain
+__builtin__.__dict__['__import__'] = _uno_import
+
+
+class _ConstantGroup(object):
+ """Represents a group of UNOIDL constants."""
+
+ __slots__ = ['_constants']
+
+ def __init__(self, constants):
+ self._constants = constants
+
+ def __dir__(self):
+ return self._constants.keys()
+
+ def __getattr__(self, name):
+ if name in self._constants:
+ return self._constants[name]
+
+ raise AttributeError("The constant '%s' could not be found." % name)
+
+
+def _impl_getConstantGroupByName(module, group):
+ """Gets UNOIDL constant group by name."""
+
+ constants = Enum('com.sun.star.uno.TypeClass', 'CONSTANTS')
+ one = Enum('com.sun.star.reflection.TypeDescriptionSearchDepth', 'ONE')
+ type_desc_mgr = _component_context.getValueByName('/singletons/com.sun.star.reflection.theTypeDescriptionManager')
+ type_descs = type_desc_mgr.createTypeDescriptionEnumeration(module, (constants,), one)
+ qualified_name = module + '.' + group
+
+ for type_desc in type_descs:
+ if type_desc.Name == qualified_name:
+ return _ConstantGroup(dict(
+ (c.Name.split('.')[-1], c.ConstantValue)
+ for c in type_desc.Constants))
+
+ raise ValueError("The constant group '%s' could not be found." % qualified_name)
+
+
+def _uno_struct__init__(self, *args, **kwargs):
+ """Initializes a UNO struct.
+
+ Referenced from the pyuno shared library.
+
+ This function can be called with either an already constructed UNO struct, which it
+ will then just reference without copying, or with arguments to create a new UNO struct.
+ """
+
+ # Check to see if this function was passed an existing UNO struct
+ if len(kwargs) == 0 and len(args) == 1 and getattr(args[0], "__class__", None) == self.__class__:
+ self.__dict__['value'] = args[0]
+ else:
+ struct, used = pyuno._createUnoStructHelper(self.__class__.__pyunostruct__, args, **kwargs)
+
+ for kwarg in kwargs.keys():
+ if not used.get(kwarg):
+ RuntimeException = pyuno.getClass("com.sun.star.uno.RuntimeException")
+ raise RuntimeException("_uno_struct__init__: unused keyword argument '%s'." % kwarg, None)
+
+ self.__dict__["value"] = struct
+
+
+def _uno_struct__getattr__(self, name):
+ """Gets attribute from UNO struct.
+
+ Referenced from the pyuno shared library.
+ """
+
+ return getattr(self.__dict__["value"], name)
+
+
+def _uno_struct__setattr__(self, name, value):
+ """Sets attribute on UNO struct.
+
+ Referenced from the pyuno shared library.
+ """
+
+ return setattr(self.__dict__["value"], name, value)
+
+
+def _uno_struct__repr__(self):
+ """Converts a UNO struct to a printable string.
+
+ Referenced from the pyuno shared library.
+ """
+
+ return repr(self.__dict__["value"])
+
+
+def _uno_struct__str__(self):
+ """Converts a UNO struct to a string."""
+
+ return str(self.__dict__["value"])
+
+def _uno_struct__ne__(self, other):
+ return not self.__eq__(other)
+
+def _uno_struct__eq__(self, that):
+ """Compares two UNO structs.
+
+ Referenced from the pyuno shared library.
+ """
+
+ if hasattr(that, "value"):
+ return self.__dict__["value"] == that.__dict__["value"]
+
+ return False
+
+
+def _uno_extract_printable_stacktrace(trace):
+ """Extracts a printable stacktrace.
+
+ Referenced from pyuno shared lib and pythonscript.py.
+ """
+
+ return ''.join(traceback.format_tb(trace))
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/source/module/unohelper.py b/pyuno/source/module/unohelper.py
new file mode 100644
index 0000000000..cf6f78d63b
--- /dev/null
+++ b/pyuno/source/module/unohelper.py
@@ -0,0 +1,297 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+import uno
+import pyuno
+import os
+import sys
+
+from com.sun.star.lang import XTypeProvider, XSingleComponentFactory, XServiceInfo
+from com.sun.star.uno import RuntimeException, XCurrentContext
+from com.sun.star.beans.MethodConcept import ALL as METHOD_CONCEPT_ALL
+from com.sun.star.beans.PropertyConcept import ALL as PROPERTY_CONCEPT_ALL
+
+from com.sun.star.reflection.ParamMode import \
+ IN as PARAM_MODE_IN, \
+ OUT as PARAM_MODE_OUT, \
+ INOUT as PARAM_MODE_INOUT
+
+from com.sun.star.beans.PropertyAttribute import \
+ MAYBEVOID as PROP_ATTR_MAYBEVOID, \
+ BOUND as PROP_ATTR_BOUND, \
+ CONSTRAINED as PROP_ATTR_CONSTRAINED, \
+ TRANSIENT as PROP_ATTR_TRANSIENT, \
+ READONLY as PROP_ATTR_READONLY, \
+ MAYBEAMBIGUOUS as PROP_ATTR_MAYBEAMBIGUOUS, \
+ MAYBEDEFAULT as PROP_ATTR_MAYBEDEFAULT, \
+ REMOVABLE as PROP_ATTR_REMOVABLE
+
+def _mode_to_str( mode ):
+ ret = "[]"
+ if mode == PARAM_MODE_INOUT:
+ ret = "[inout]"
+ elif mode == PARAM_MODE_OUT:
+ ret = "[out]"
+ elif mode == PARAM_MODE_IN:
+ ret = "[in]"
+ return ret
+
+def _propertymode_to_str( mode ):
+ ret = ""
+ if PROP_ATTR_REMOVABLE & mode:
+ ret = ret + "removable "
+ if PROP_ATTR_MAYBEDEFAULT & mode:
+ ret = ret + "maybedefault "
+ if PROP_ATTR_MAYBEAMBIGUOUS & mode:
+ ret = ret + "maybeambiguous "
+ if PROP_ATTR_READONLY & mode:
+ ret = ret + "readonly "
+ if PROP_ATTR_TRANSIENT & mode:
+ ret = ret + "transient "
+ if PROP_ATTR_CONSTRAINED & mode:
+ ret = ret + "constrained "
+ if PROP_ATTR_BOUND & mode:
+ ret = ret + "bound "
+ if PROP_ATTR_MAYBEVOID & mode:
+ ret = ret + "maybevoid "
+ return ret.rstrip()
+
+def inspect( obj , out ):
+ if isinstance( obj, uno.Type ) or \
+ isinstance( obj, uno.Char ) or \
+ isinstance( obj, uno.Bool ) or \
+ isinstance( obj, uno.ByteSequence ) or \
+ isinstance( obj, uno.Enum ) or \
+ isinstance( obj, uno.Any ):
+ out.write( str(obj) + "\n")
+ return
+
+ ctx = uno.getComponentContext()
+ introspection = \
+ ctx.ServiceManager.createInstanceWithContext( "com.sun.star.beans.Introspection", ctx )
+
+ out.write( "Supported services:\n" )
+ if hasattr( obj, "getSupportedServiceNames" ):
+ names = obj.getSupportedServiceNames()
+ for ii in names:
+ out.write( " " + ii + "\n" )
+ else:
+ out.write( " unknown\n" )
+
+ out.write( "Interfaces:\n" )
+ if hasattr( obj, "getTypes" ):
+ interfaces = obj.getTypes()
+ for ii in interfaces:
+ out.write( " " + ii.typeName + "\n" )
+ else:
+ out.write( " unknown\n" )
+
+ access = introspection.inspect( obj )
+ methods = access.getMethods( METHOD_CONCEPT_ALL )
+ out.write( "Methods:\n" )
+ for ii in methods:
+ out.write( " " + ii.ReturnType.Name + " " + ii.Name )
+ args = ii.ParameterTypes
+ infos = ii.ParameterInfos
+ out.write( "( " )
+ for i in range( 0, len( args ) ):
+ if i > 0:
+ out.write( ", " )
+ out.write( _mode_to_str( infos[i].aMode ) + " " + args[i].Name + " " + infos[i].aName )
+ out.write( " )\n" )
+
+ props = access.getProperties( PROPERTY_CONCEPT_ALL )
+ out.write ("Properties:\n" )
+ for ii in props:
+ out.write( " ("+_propertymode_to_str( ii.Attributes ) + ") "+ii.Type.typeName+" "+ii.Name+ "\n" )
+
+def createSingleServiceFactory( clazz, implementationName, serviceNames ):
+ return _FactoryHelper_( clazz, implementationName, serviceNames )
+
+class _ImplementationHelperEntry:
+ def __init__(self, ctor,serviceNames):
+ self.ctor = ctor
+ self.serviceNames = serviceNames
+
+class ImplementationHelper:
+ def __init__(self):
+ self.impls = {}
+
+ def addImplementation( self, ctor, implementationName, serviceNames ):
+ self.impls[implementationName] = _ImplementationHelperEntry(ctor,serviceNames)
+
+ def writeRegistryInfo( self, regKey, smgr ):
+ for i in list(self.impls.items()):
+ keyName = "/"+ i[0] + "/UNO/SERVICES"
+ key = regKey.createKey( keyName )
+ for serviceName in i[1].serviceNames:
+ key.createKey( serviceName )
+ return 1
+
+ def getComponentFactory( self, implementationName , regKey, smgr ):
+ entry = self.impls.get( implementationName, None )
+ if entry is None:
+ raise RuntimeException( implementationName + " is unknown" , None )
+ return createSingleServiceFactory( entry.ctor, implementationName, entry.serviceNames )
+
+ def getSupportedServiceNames( self, implementationName ):
+ entry = self.impls.get( implementationName, None )
+ if entry is None:
+ raise RuntimeException( implementationName + " is unknown" , None )
+ return entry.serviceNames
+
+ def supportsService( self, implementationName, serviceName ):
+ entry = self.impls.get( implementationName,None )
+ if entry is None:
+ raise RuntimeException( implementationName + " is unknown", None )
+ return serviceName in entry.serviceNames
+
+
+class ImplementationEntry:
+ def __init__(self, implName, supportedServices, clazz ):
+ self.implName = implName
+ self.supportedServices = supportedServices
+ self.clazz = clazz
+
+def writeRegistryInfoHelper( smgr, regKey, seqEntries ):
+ for entry in seqEntries:
+ keyName = "/"+ entry.implName + "/UNO/SERVICES"
+ key = regKey.createKey( keyName )
+ for serviceName in entry.supportedServices:
+ key.createKey( serviceName )
+
+def systemPathToFileUrl( systemPath ):
+ "returns a file-url for the given system path"
+ return pyuno.systemPathToFileUrl( systemPath )
+
+def fileUrlToSystemPath( url ):
+ "returns a system path (determined by the system, the python interpreter is running on)"
+ return pyuno.fileUrlToSystemPath( url )
+
+def absolutize( path, relativeUrl ):
+ "returns an absolute file url from the given urls"
+ return pyuno.absolutize( path, relativeUrl )
+
+def getComponentFactoryHelper( implementationName, smgr, regKey, seqEntries ):
+ for x in seqEntries:
+ if x.implName == implementationName:
+ return createSingleServiceFactory( x.clazz, implementationName, x.supportedServices )
+
+def addComponentsToContext( toBeExtendedContext, contextRuntime, componentUrls, loaderName ):
+ smgr = contextRuntime.ServiceManager
+ loader = smgr.createInstanceWithContext( loaderName, contextRuntime )
+ implReg = smgr.createInstanceWithContext( "com.sun.star.registry.ImplementationRegistration",contextRuntime)
+
+ isWin = os.name == 'nt' or os.name == 'dos'
+ isMac = sys.platform == 'darwin'
+ # create a temporary registry
+ for componentUrl in componentUrls:
+ reg = smgr.createInstanceWithContext( "com.sun.star.registry.SimpleRegistry", contextRuntime )
+ reg.open( "", 0, 1 )
+ if not isWin and componentUrl.endswith( ".uno" ): # still allow platform independent naming
+ if isMac:
+ componentUrl = componentUrl + ".dylib"
+ else:
+ componentUrl = componentUrl + ".so"
+
+ implReg.registerImplementation( loaderName,componentUrl, reg )
+ rootKey = reg.getRootKey()
+ implementationKey = rootKey.openKey( "IMPLEMENTATIONS" )
+ implNames = implementationKey.getKeyNames()
+ extSMGR = toBeExtendedContext.ServiceManager
+ for x in implNames:
+ fac = loader.activate( max(x.split("/")),"",componentUrl,rootKey)
+ extSMGR.insert( fac )
+ reg.close()
+
+# never shrinks !
+_g_typeTable = {}
+def _unohelper_getHandle( self):
+ ret = None
+ if self.__class__ in _g_typeTable:
+ ret = _g_typeTable[self.__class__]
+ else:
+ names = {}
+ traverse = list(self.__class__.__bases__)
+ while len( traverse ) > 0:
+ item = traverse.pop()
+ bases = item.__bases__
+ if uno.isInterface( item ):
+ names[item.__pyunointerface__] = None
+ elif len(bases) > 0:
+ # the "else if", because we only need the most derived interface
+ traverse = traverse + list(bases)#
+
+ lst = list(names.keys())
+ types = []
+ for x in lst:
+ t = uno.getTypeByName( x )
+ types.append( t )
+
+ ret = tuple(types)
+ _g_typeTable[self.__class__] = ret
+ return ret
+
+class Base(XTypeProvider):
+ def getTypes( self ):
+ return _unohelper_getHandle( self )
+ def getImplementationId(self):
+ return ()
+
+class CurrentContext(XCurrentContext, Base ):
+ """a current context implementation, which first does a lookup in the given
+ hashmap and if the key cannot be found, it delegates to the predecessor
+ if available
+ """
+ def __init__( self, oldContext, hashMap ):
+ self.hashMap = hashMap
+ self.oldContext = oldContext
+
+ def getValueByName( self, name ):
+ if name in self.hashMap:
+ return self.hashMap[name]
+ elif self.oldContext is not None:
+ return self.oldContext.getValueByName( name )
+ else:
+ return None
+
+# -------------------------------------------------
+# implementation details
+# -------------------------------------------------
+class _FactoryHelper_( XSingleComponentFactory, XServiceInfo, Base ):
+ def __init__( self, clazz, implementationName, serviceNames ):
+ self.clazz = clazz
+ self.implementationName = implementationName
+ self.serviceNames = serviceNames
+
+ def getImplementationName( self ):
+ return self.implementationName
+
+ def supportsService( self, ServiceName ):
+ return ServiceName in self.serviceNames
+
+ def getSupportedServiceNames( self ):
+ return self.serviceNames
+
+ def createInstanceWithContext( self, context ):
+ return self.clazz( context )
+
+ def createInstanceWithArgumentsAndContext( self, args, context ):
+ return self.clazz( context, *args )
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/source/officehelper.py b/pyuno/source/officehelper.py
new file mode 100644
index 0000000000..53bd5943e3
--- /dev/null
+++ b/pyuno/source/officehelper.py
@@ -0,0 +1,87 @@
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+#
+# Translated to python from "Bootstrap.java" by Kim Kulak
+#
+
+import os
+import random
+from sys import platform
+from time import sleep
+
+import uno
+from com.sun.star.connection import NoConnectException
+from com.sun.star.uno import Exception as UnoException
+
+
+class BootstrapException(UnoException):
+ pass
+
+def bootstrap():
+ """Bootstrap OOo and PyUNO Runtime.
+ The soffice process is started opening a named pipe of random name, then the local context is used
+ to access the pipe. This function directly returns the remote component context, from whereon you can
+ get the ServiceManager by calling getServiceManager() on the returned object.
+ """
+ try:
+ # soffice script used on *ix, Mac; soffice.exe used on Win
+ if "UNO_PATH" in os.environ:
+ sOffice = os.environ["UNO_PATH"]
+ else:
+ sOffice = "" # lets hope for the best
+ sOffice = os.path.join(sOffice, "soffice")
+ if platform.startswith("win"):
+ sOffice += ".exe"
+
+ # Generate a random pipe name.
+ random.seed()
+ sPipeName = "uno" + str(random.random())[2:]
+
+ # Start the office process, don't check for exit status since an exception is caught anyway if the office terminates unexpectedly.
+ cmdArray = (sOffice, "--nologo", "--nodefault", "".join(["--accept=pipe,name=", sPipeName, ";urp;"]))
+ os.spawnv(os.P_NOWAIT, sOffice, cmdArray)
+
+ # ---------
+
+ xLocalContext = uno.getComponentContext()
+ resolver = xLocalContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", xLocalContext)
+ sConnect = "".join(["uno:pipe,name=", sPipeName, ";urp;StarOffice.ComponentContext"])
+
+ # Wait until an office is started, but loop only nLoop times (can we do this better???)
+ nLoop = 20
+ while True:
+ try:
+ xContext = resolver.resolve(sConnect)
+ break
+ except NoConnectException:
+ nLoop -= 1
+ if nLoop <= 0:
+ raise BootstrapException("Cannot connect to soffice server.", None)
+ sleep(0.5) # Sleep 1/2 second.
+
+ except BootstrapException:
+ raise
+ except Exception as e: # Any other exception
+ raise BootstrapException("Caught exception " + str(e), None)
+
+ return xContext
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab: