offapi/UnoApi_offapi.mk | 2 offapi/org/libreoffice/embindtest/XStringFactory.idl | 18 ++++++ offapi/org/libreoffice/embindtest/theSingleton.idl | 16 +++++ pyuno/PythonTest_pyuno_pytests_embindtest.mk | 1 pyuno/qa/pytests/singleton.py | 20 +++++++ pyuno/source/module/pyuno_except.cxx | 36 +++++++++++- pyuno/source/module/uno.py | 11 +++ unotest/Library_embindtest.mk | 1 unotest/source/embindtest/embindtest.component | 6 ++ unotest/source/embindtest/singleton.cxx | 53 +++++++++++++++++++ 10 files changed, 158 insertions(+), 6 deletions(-)
New commits: commit 8b022d0d0d08e3133f37d6e088b4891deeb1b52c Author: Neil Roberts <[email protected]> AuthorDate: Wed Feb 11 21:57:31 2026 +0100 Commit: Stephan Bergmann <[email protected]> CommitDate: Wed Mar 4 13:53:40 2026 +0100 pyuno: Add a unit test for singletons As with the test for services, this uses the embindtest framework to hide it behind --enable-embindtest-uno. Change-Id: Ie64848926d21ca5c3edd449de85f4fed16b3b2aa Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199220 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 356e97893dcc..3e5d5b1f40e4 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -4399,7 +4399,9 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,org/libreoffice/embindtest, \ Template \ XArgumentStore \ XAttributes \ + XStringFactory \ XTest \ + theSingleton \ )) $(eval $(call gb_UnoApi_add_idlfiles_nohdl,offapi,org/libreoffice/embindtest, \ BridgeTest \ diff --git a/offapi/org/libreoffice/embindtest/XStringFactory.idl b/offapi/org/libreoffice/embindtest/XStringFactory.idl new file mode 100644 index 000000000000..9a69b3df72c1 --- /dev/null +++ b/offapi/org/libreoffice/embindtest/XStringFactory.idl @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +module org { module libreoffice { module embindtest { + +interface XStringFactory { + string getString(); +}; + +}; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/offapi/org/libreoffice/embindtest/theSingleton.idl b/offapi/org/libreoffice/embindtest/theSingleton.idl new file mode 100644 index 000000000000..1168f5993350 --- /dev/null +++ b/offapi/org/libreoffice/embindtest/theSingleton.idl @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +module org { module libreoffice { module embindtest { + +singleton theSingleton: XStringFactory; + +}; }; }; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/pyuno/PythonTest_pyuno_pytests_embindtest.mk b/pyuno/PythonTest_pyuno_pytests_embindtest.mk index 053b05f1d2d1..978e06ecf15e 100644 --- a/pyuno/PythonTest_pyuno_pytests_embindtest.mk +++ b/pyuno/PythonTest_pyuno_pytests_embindtest.mk @@ -12,6 +12,7 @@ $(eval $(call gb_PythonTest_PythonTest,pyuno_pytests_embindtest)) $(eval $(call gb_PythonTest_add_modules,pyuno_pytests_embindtest,$(SRCDIR)/pyuno/qa/pytests, \ embindtest \ serviceconstructors \ + singleton \ )) # vim: set noet sw=4 ts=4: diff --git a/pyuno/qa/pytests/singleton.py b/pyuno/qa/pytests/singleton.py new file mode 100644 index 000000000000..bcc9b7cfe26a --- /dev/null +++ b/pyuno/qa/pytests/singleton.py @@ -0,0 +1,20 @@ +# -*- 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/. +# + +import unittest +import org.libreoffice.unotest +from org.libreoffice.embindtest import theSingleton + +class SingletonTest(unittest.TestCase): + def test_singleton(self): + ctx = org.libreoffice.unotest.pyuno.getComponentContext() + xStringFactory = theSingleton.get(ctx) + self.assertEqual(xStringFactory.getString(), "this is a string from XStringFactory") + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/unotest/Library_embindtest.mk b/unotest/Library_embindtest.mk index 4c7a92f4d5c1..d9dae0932187 100644 --- a/unotest/Library_embindtest.mk +++ b/unotest/Library_embindtest.mk @@ -12,6 +12,7 @@ $(eval $(call gb_Library_Library,embindtest)) $(eval $(call gb_Library_add_exception_objects,embindtest, \ unotest/source/embindtest/embindtest \ unotest/source/embindtest/serviceconstructors \ + unotest/source/embindtest/singleton \ )) $(eval $(call gb_Library_set_componentfile,embindtest,unotest/source/embindtest/embindtest,services)) diff --git a/unotest/source/embindtest/embindtest.component b/unotest/source/embindtest/embindtest.component index 30bdc5c3f68f..06d58ed2acb5 100644 --- a/unotest/source/embindtest/embindtest.component +++ b/unotest/source/embindtest/embindtest.component @@ -28,4 +28,10 @@ <service name="org.libreoffice.embindtest.ImplicitConstructor"/> <service name="org.libreoffice.embindtest.ExplicitConstructors"/> </implementation> + <implementation + constructor="org_libreoffice_comp_embindtest_Singleton_get_implementation" + name="org.libreoffice.comp.embindtest.theSingleton" + single-instance="true"> + <singleton name="org.libreoffice.embindtest.theSingleton"/> + </implementation> </component> diff --git a/unotest/source/embindtest/singleton.cxx b/unotest/source/embindtest/singleton.cxx new file mode 100644 index 000000000000..e89879a67eae --- /dev/null +++ b/unotest/source/embindtest/singleton.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <sal/config.h> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <org/libreoffice/embindtest/XStringFactory.hpp> + +namespace com::sun::star::uno +{ +class XComponentContext; +} + +namespace +{ +class SingletonTest : public cppu::WeakImplHelper<org::libreoffice::embindtest::XStringFactory, + css::lang::XServiceInfo> +{ +public: + OUString SAL_CALL getImplementationName() override + { + return u"org.libreoffice.comp.embindtest.Singleton"_ustr; + } + + sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { u"org.libreoffice.embindtest.Singleton"_ustr }; + } + + OUString SAL_CALL getString() override { return "this is a string from XStringFactory"; } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_libreoffice_comp_embindtest_Singleton_get_implementation( + css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new SingletonTest); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ commit 45d2412c7809db3649ceef1c3c3f538693ed9069 Author: Neil Roberts <[email protected]> AuthorDate: Wed Feb 11 16:40:48 2026 +0100 Commit: Stephan Bergmann <[email protected]> CommitDate: Wed Mar 4 13:53:28 2026 +0100 tdf#171122 pyuno: Add class getter methods for singletons Makes it possible to import the name of a singleton into a Python script as a class and then retrieve it with a getter method. Eg, instead of: desktop = ctx.getValueByName( "/singletons/com.sun.star.frame.theDesktop") you can now do: from com.sun.star.frame import theDesktop desktop = theDesktop.get(ctx) Change-Id: I009bd2e890cde52aa6273e462621157fc1f63661 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199219 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> diff --git a/pyuno/source/module/pyuno_except.cxx b/pyuno/source/module/pyuno_except.cxx index c3d66f84b4d5..1097c2f193a5 100644 --- a/pyuno/source/module/pyuno_except.cxx +++ b/pyuno/source/module/pyuno_except.cxx @@ -21,6 +21,7 @@ #include <typelib/typedescription.hxx> #include <com/sun/star/container/XHierarchicalNameAccess.hpp> #include <com/sun/star/reflection/XServiceTypeDescription2.hpp> +#include <com/sun/star/reflection/XSingletonTypeDescription.hpp> #include <com/sun/star/script/CannotConvertException.hpp> @@ -221,6 +222,23 @@ static PyRef createClassForService( return ret; } +static PyRef createClassForSingleton( + const css::uno::Reference<css::reflection::XSingletonTypeDescription>& xSingleton, + const Runtime& runtime) +{ + PyRef ret = createEmptyPyTypeForTypeDescription(xSingleton); + + // Set “get” to be a class method to get the singleton given a single context parameter. The + // name of the service is stored via a closure. + OUString singletonName = xSingleton->getName(); + PyRef makeGetter = getObjectFromUnoModule(runtime, "_uno_make_singleton_getter"); + PyRef getter = PyObject_CallOneArg(makeGetter.get(), ustring2PyString(singletonName).get()); + + PyObject_SetAttrString(ret.get(), "get", getter.get()); + + return ret; +} + /// @throws RuntimeException static PyRef createClass( const OUString & name, const Runtime &runtime ) { @@ -230,8 +248,8 @@ static PyRef createClass( const OUString & name, const Runtime &runtime ) if (desc.is()) return createClassFromTypeDescription(name, desc.get(), runtime); - // If there’s no type description from the typelib then check if it’s a service using the type - // description manager. + // If there’s no type description from the typelib then check if it’s a service or a singleton + // using the type description manager. css::uno::Any xType; try @@ -243,10 +261,18 @@ static PyRef createClass( const OUString & name, const Runtime &runtime ) // This will flow through to throw a runtime exception below } - css::uno::Reference<css::reflection::XServiceTypeDescription2> xService; + if (xType.hasValue()) + { + css::uno::Reference<css::reflection::XServiceTypeDescription2> xService; + + if ((xType >>= xService) && xService.is()) + return createClassForService(xService); - if ((xType >>= xService) && xService.is()) - return createClassForService(xService); + css::uno::Reference<css::reflection::XSingletonTypeDescription> xSingleton; + + if ((xType >>= xSingleton) && xSingleton.is()) + return createClassForSingleton(xSingleton, runtime); + } throw RuntimeException("pyuno.getClass: uno exception " + name + " is unknown"); } diff --git a/pyuno/source/module/uno.py b/pyuno/source/module/uno.py index 5cb6e6de9385..698fef9590b5 100644 --- a/pyuno/source/module/uno.py +++ b/pyuno/source/module/uno.py @@ -384,7 +384,7 @@ def _uno_import(name, *optargs, **kwargs): failed = False try: - # check for structs, exceptions, interfaces or services + # check for structs, exceptions, interfaces, services or singletons d[class_name] = pyuno.getClass(name + "." + class_name) except RuntimeException: # check for enums @@ -557,4 +557,13 @@ def _uno_extract_printable_stacktrace(trace): return ''.join(traceback.format_tb(trace)) +def _uno_make_singleton_getter(singleton_name): + value_name = "/singletons/" + singleton_name + + @classmethod + def get(_cls, ctx): + return ctx.getValueByName(value_name) + + return get + # vim: set shiftwidth=4 softtabstop=4 expandtab:
