summaryrefslogtreecommitdiffstats
path: root/pyuno
diff options
context:
space:
mode:
Diffstat (limited to 'pyuno')
-rw-r--r--pyuno/CustomTarget_python_shell.mk40
-rw-r--r--pyuno/CustomTarget_pyuno_pythonloader_ini.mk38
-rw-r--r--pyuno/Executable_python.mk22
-rw-r--r--pyuno/IwyuFilter_pyuno.yaml37
-rw-r--r--pyuno/Library_pythonloader.mk40
-rw-r--r--pyuno/Library_pyuno.mk52
-rw-r--r--pyuno/Library_pyuno_wrapper.mk48
-rw-r--r--pyuno/Makefile14
-rw-r--r--pyuno/Module_pyuno.mk62
-rw-r--r--pyuno/Package_python_scripts.mk19
-rw-r--r--pyuno/Package_python_shell.mk18
-rw-r--r--pyuno/Package_pyuno_pythonloader_ini.mk16
-rw-r--r--pyuno/PythonTest_pyuno_pytests_insertremovecells.mk20
-rw-r--r--pyuno/PythonTest_pyuno_pytests_testcollections.mk27
-rw-r--r--pyuno/PythonTest_pyuno_pytests_testssl.mk16
-rw-r--r--pyuno/README.md22
-rw-r--r--pyuno/Rdb_pyuno.mk12
-rw-r--r--pyuno/demo/Addons.xcu36
-rw-r--r--pyuno/demo/biblioaccess.py53
-rw-r--r--pyuno/demo/hello_world_comp.py59
-rw-r--r--pyuno/demo/makefile.mk60
-rw-r--r--pyuno/demo/ooextract.py128
-rw-r--r--pyuno/demo/pyunoenv.bat23
-rw-r--r--pyuno/demo/pyunoenv.tcsh49
-rw-r--r--pyuno/demo/swriter.py118
-rw-r--r--pyuno/demo/swritercomp.py128
-rw-r--r--pyuno/demo/swritercompclient.py32
-rw-r--r--pyuno/doc/modes.sxdbin0 -> 7181 bytes
-rw-r--r--pyuno/inc/pyuno.hxx321
-rw-r--r--pyuno/qa/pytests/insertremovecells.py77
-rw-r--r--pyuno/qa/pytests/testcollections_XCellRange.py399
-rw-r--r--pyuno/qa/pytests/testcollections_XEnumeration.py120
-rw-r--r--pyuno/qa/pytests/testcollections_XEnumerationAccess.py144
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexAccess.py324
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexContainer.py204
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexReplace.py240
-rw-r--r--pyuno/qa/pytests/testcollections_XNameAccess.py204
-rw-r--r--pyuno/qa/pytests/testcollections_XNameContainer.py131
-rw-r--r--pyuno/qa/pytests/testcollections_XNameReplace.py78
-rw-r--r--pyuno/qa/pytests/testcollections_base.py62
-rw-r--r--pyuno/qa/pytests/testcollections_misc.py81
-rw-r--r--pyuno/qa/pytests/testcollections_misc2.py61
-rw-r--r--pyuno/qa/pytests/testcollections_mixednameindex.py48
-rw-r--r--pyuno/qa/pytests/testdocuments/fdo74824.odsbin0 -> 4811 bytes
-rw-r--r--pyuno/qa/pytests/testssl.py10
-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
-rw-r--r--pyuno/zipcore/mac.sh14
-rw-r--r--pyuno/zipcore/nonmac.sh12
-rw-r--r--pyuno/zipcore/python.cxx212
-rwxr-xr-xpyuno/zipcore/python.sh40
68 files changed, 11673 insertions, 0 deletions
diff --git a/pyuno/CustomTarget_python_shell.mk b/pyuno/CustomTarget_python_shell.mk
new file mode 100644
index 0000000000..541620304d
--- /dev/null
+++ b/pyuno/CustomTarget_python_shell.mk
@@ -0,0 +1,40 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,pyuno/python_shell))
+
+$(eval $(call gb_CustomTarget_register_targets,pyuno/python_shell,\
+ os.sh \
+ python.sh \
+))
+
+ifeq ($(OS),MACOSX)
+pyuno_PYTHON_SHELL_VERSION:=$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)
+else
+pyuno_PYTHON_SHELL_VERSION:=$(PYTHON_VERSION)
+endif
+
+$(call gb_CustomTarget_get_workdir,pyuno/python_shell)/python.sh : \
+ $(SRCDIR)/pyuno/zipcore/python.sh \
+ $(call gb_CustomTarget_get_workdir,pyuno/python_shell)/os.sh
+ $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),CAT,1)
+ $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),CAT)
+ cat $^ > $@ && chmod +x $@
+ $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),CAT)
+
+$(call gb_CustomTarget_get_workdir,pyuno/python_shell)/os.sh : \
+ $(SRCDIR)/pyuno/zipcore/$(if $(filter MACOSX,$(OS)),mac,nonmac).sh \
+ $(BUILDDIR)/config_$(gb_Side)/config_python.h
+ $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),SED,1)
+ $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),SED)
+ sed -e "s/%%PYVERSION%%/$(pyuno_PYTHON_SHELL_VERSION)/g" \
+ $< > $@
+ $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),SED)
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/CustomTarget_pyuno_pythonloader_ini.mk b/pyuno/CustomTarget_pyuno_pythonloader_ini.mk
new file mode 100644
index 0000000000..cc5a67f3ca
--- /dev/null
+++ b/pyuno/CustomTarget_pyuno_pythonloader_ini.mk
@@ -0,0 +1,38 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,pyuno/pythonloader_ini))
+
+$(eval $(call gb_CustomTarget_register_targets,pyuno/pythonloader_ini, \
+ $(call gb_Helper_get_rcfile,pythonloader.uno) \
+))
+
+$(call gb_CustomTarget_get_workdir,pyuno/pythonloader_ini)/$(call gb_Helper_get_rcfile,pythonloader.uno): \
+ $(BUILDDIR)/config_$(gb_Side)/config_python.h \
+ $(SRCDIR)/pyuno/CustomTarget_pyuno_pythonloader_ini.mk
+ $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),ECH,1)
+ $(call gb_Trace_StartRange,$(subst $(WORKDIR)/,,$@),ECH)
+ ( printf '[Bootstrap]\n' && \
+ $(if $(SYSTEM_PYTHON),, \
+ printf 'PYUNO_LOADER_PYTHONHOME=%s\n' \
+ $(if $(filter MACOSX,$(OS)), \
+ '$$ORIGIN/../Frameworks/LibreOfficePython.framework', \
+ '$$ORIGIN/python-core-$(PYTHON_VERSION)') &&) \
+ printf 'PYUNO_LOADER_PYTHONPATH=%s$$ORIGIN\n' \
+ $(if $(SYSTEM_PYTHON), \
+ '', \
+ $(if $(filter MACOSX,$(OS)), \
+ '$(foreach dir,/ /lib-dynload /lib-tk /site-packages,$(patsubst %/,%,$$ORIGIN/../Frameworks/LibreOfficePython.framework/Versions/Current/lib/python$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)$(dir))) ', \
+ $(if $(filter WNT,$(OS)), \
+ '$(foreach dir,/ /site-packages,$(patsubst %/,%,$$ORIGIN/python-core-$(PYTHON_VERSION)/lib$(dir))) ', \
+ '$(foreach dir,/ /lib-dynload /lib-tk /site-packages,$(patsubst %/,%,$$ORIGIN/python-core-$(PYTHON_VERSION)/lib$(dir))) '))) \
+ ) > $@
+ $(call gb_Trace_EndRange,$(subst $(WORKDIR)/,,$@),ECH)
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/Executable_python.mk b/pyuno/Executable_python.mk
new file mode 100644
index 0000000000..192d54cf9e
--- /dev/null
+++ b/pyuno/Executable_python.mk
@@ -0,0 +1,22 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,python))
+
+$(eval $(call gb_Executable_use_static_libraries,python,\
+ ooopathutils \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,python,\
+ pyuno/zipcore/python \
+))
+
+$(eval $(call gb_Executable_add_default_nativeres,python))
+
+# vim:set noet sw=4 ts=4:
diff --git a/pyuno/IwyuFilter_pyuno.yaml b/pyuno/IwyuFilter_pyuno.yaml
new file mode 100644
index 0000000000..6e3a5df38b
--- /dev/null
+++ b/pyuno/IwyuFilter_pyuno.yaml
@@ -0,0 +1,37 @@
+---
+assumeFilename: pyuno/source/module/pyuno.cxx
+excludelist:
+ pyuno/inc/pyuno.hxx:
+ # Used in #ifdef
+ - Python.h
+ pyuno/source/module/pyuno_impl.hxx:
+ # Used in #ifdef
+ - Python.h
+ # Base class needs complete type
+ - com/sun/star/lang/XUnoTunnel.hpp
+ - com/sun/star/script/XInvocation.hpp
+ # Needed for typedef
+ - com/sun/star/container/XIndexAccess.hpp
+ - cppuhelper/weakref.hxx
+ pyuno/source/loader/pyuno_loader.cxx:
+ # Don't replace with URE impl. detail
+ - osl/file.hxx
+ # Needed for direct member access
+ - com/sun/star/uno/XComponentContext.hpp
+ # Needed on WIN32
+ - o3tl/char16_t2wchar_t.hxx
+ pyuno/source/module/pyuno_module.cxx:
+ # Needed on MACOSX
+ - config_folders.h
+ # Don't replace with URE impl. detail
+ - rtl/bootstrap.hxx
+ pyuno/source/module/pyuno_runtime.cxx:
+ # Needed on MACOSX
+ - config_folders.h
+ # Needed for direct member access
+ - com/sun/star/container/XHierarchicalNameAccess.hpp
+ # Needed for template
+ - com/sun/star/script/XInvocation2.hpp
+ pyuno/zipcore/python.cxx:
+ # Needed on WIN32
+ - tools/pathutils.hxx
diff --git a/pyuno/Library_pythonloader.mk b/pyuno/Library_pythonloader.mk
new file mode 100644
index 0000000000..4e3776b848
--- /dev/null
+++ b/pyuno/Library_pythonloader.mk
@@ -0,0 +1,40 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,pythonloader))
+
+$(eval $(call gb_Library_set_componentfile,pythonloader,pyuno/source/loader/pythonloader,pyuno))
+
+$(eval $(call gb_Library_set_include,pythonloader,\
+ -I$(SRCDIR)/pyuno/source/loader \
+ -I$(SRCDIR)/pyuno/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_use_api,pythonloader,\
+ udkapi \
+))
+
+$(eval $(call gb_Library_use_libraries,pythonloader,\
+ cppu \
+ cppuhelper \
+ pyuno \
+ sal \
+))
+
+$(eval $(call gb_Library_use_externals,pythonloader,\
+ boost_headers \
+ python \
+))
+
+$(eval $(call gb_Library_add_exception_objects,pythonloader,\
+ pyuno/source/loader/pyuno_loader \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/pyuno/Library_pyuno.mk b/pyuno/Library_pyuno.mk
new file mode 100644
index 0000000000..49c5b9104e
--- /dev/null
+++ b/pyuno/Library_pyuno.mk
@@ -0,0 +1,52 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,pyuno))
+
+$(eval $(call gb_Library_set_include,pyuno,\
+ -I$(SRCDIR)/pyuno/source/loader \
+ -I$(SRCDIR)/pyuno/inc \
+ $$(INCLUDE) \
+))
+
+$(eval $(call gb_Library_add_defs,pyuno,\
+ -DLO_DLLIMPLEMENTATION_PYUNO \
+))
+
+$(eval $(call gb_Library_use_api,pyuno,\
+ udkapi \
+))
+
+$(eval $(call gb_Library_use_libraries,pyuno,\
+ cppu \
+ cppuhelper \
+ sal \
+ salhelper \
+))
+
+$(eval $(call gb_Library_use_externals,pyuno,\
+ boost_headers \
+ python \
+))
+
+$(eval $(call gb_Library_add_exception_objects,pyuno,\
+ pyuno/source/module/pyuno_runtime \
+ pyuno/source/module/pyuno \
+ pyuno/source/module/pyuno_struct \
+ pyuno/source/module/pyuno_callable \
+ pyuno/source/module/pyuno_module \
+ pyuno/source/module/pyuno_type \
+ pyuno/source/module/pyuno_util \
+ pyuno/source/module/pyuno_except \
+ pyuno/source/module/pyuno_adapter \
+ pyuno/source/module/pyuno_gc \
+ pyuno/source/module/pyuno_iterator \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/pyuno/Library_pyuno_wrapper.mk b/pyuno/Library_pyuno_wrapper.mk
new file mode 100644
index 0000000000..4bce8fc00b
--- /dev/null
+++ b/pyuno/Library_pyuno_wrapper.mk
@@ -0,0 +1,48 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,pyuno_wrapper))
+
+$(eval $(call gb_Library_set_include,pyuno_wrapper,\
+ -I$(SRCDIR)/pyuno/source/module \
+ -I$(SRCDIR)/pyuno/inc \
+ $$(INCLUDE) \
+))
+
+# not using external "python" because we do not want to link against python
+$(eval $(call gb_Library_use_externals,pyuno_wrapper,\
+ python_headers \
+))
+
+# python "import pyuno" dlopens pyuno.so as RTLD_LOCAL, so g++ exception
+# handling used to not work, so pyuno.so (pyuno_wrapper) is just a thin wrapper
+# that dlopens libpyuno.so as RTLD_GLOBAL; but when pyuno.so wrapper links
+# against libstdc++ (which has not previously been loaded into python process),
+# that resolves its _ZNSs4_Rep20_S_empty_rep_storageE to itself, but later LO
+# libs (loaded though RTLD_GLOBAL libpyuno.so) may resolve that symbol to e.g.
+# cppu, because they happen to see that before libstdc++; so the requirement has
+# always been that RTLD_LOCAL-loaded pyuno.so wrapper implicitly load into the
+# process as little as possible.
+# To ensure that pyuno.so does not link against libstdc++ the dynamic link
+# command invokes gcc and not g++ if there are only C objects, so
+# don't add any C++ objects here!
+
+ifeq ($(filter DRAGONFLY FREEBSD NETBSD OPENBSD MACOSX,$(OS)),)
+
+$(eval $(call gb_Library_add_libs,pyuno_wrapper,\
+ -ldl \
+))
+
+endif
+
+$(eval $(call gb_Library_add_cobjects,pyuno_wrapper,\
+ pyuno/source/module/pyuno_dlopenwrapper \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/pyuno/Makefile b/pyuno/Makefile
new file mode 100644
index 0000000000..0997e62848
--- /dev/null
+++ b/pyuno/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/Module_pyuno.mk b/pyuno/Module_pyuno.mk
new file mode 100644
index 0000000000..71d7b48e8b
--- /dev/null
+++ b/pyuno/Module_pyuno.mk
@@ -0,0 +1,62 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Module_Module,pyuno))
+
+ifneq ($(DISABLE_PYTHON),TRUE)
+
+$(eval $(call gb_Module_add_targets,pyuno,\
+ CustomTarget_pyuno_pythonloader_ini \
+ Library_pyuno \
+ Library_pythonloader \
+ Package_python_scripts \
+ Package_pyuno_pythonloader_ini \
+ Rdb_pyuno \
+))
+
+ifneq ($(OS),WNT)
+$(eval $(call gb_Module_add_targets,pyuno,\
+ Library_pyuno_wrapper \
+))
+endif
+
+# Windows: only --enable-python=internal possible
+# python-core: pyuno/python.exe on Windows
+ifeq ($(OS),WNT)
+$(eval $(call gb_Module_add_targets,pyuno,\
+ Executable_python \
+))
+endif
+
+ifeq ($(SYSTEM_PYTHON),)
+
+# python-core: python.sh on Unix
+ifneq ($(OS),WNT)
+$(eval $(call gb_Module_add_targets,pyuno,\
+ CustomTarget_python_shell \
+ Package_python_shell \
+))
+endif
+
+endif # SYSTEM_PYTHON
+
+ifneq ($(OS),MACOSX)
+$(eval $(call gb_Module_add_check_targets,pyuno, \
+ PythonTest_pyuno_pytests_testssl \
+))
+endif
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,pyuno, \
+ PythonTest_pyuno_pytests_testcollections \
+ PythonTest_pyuno_pytests_insertremovecells \
+))
+
+endif # DISABLE_PYTHON
+
+# vim:set noet sw=4 ts=4:
diff --git a/pyuno/Package_python_scripts.mk b/pyuno/Package_python_scripts.mk
new file mode 100644
index 0000000000..0588af3705
--- /dev/null
+++ b/pyuno/Package_python_scripts.mk
@@ -0,0 +1,19 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Package_Package,pyuno_python_scripts,$(SRCDIR)/pyuno/source))
+
+$(eval $(call gb_Package_add_files,pyuno_python_scripts,$(LIBO_LIB_PYUNO_FOLDER),\
+ loader/pythonloader.py \
+ module/uno.py \
+ module/unohelper.py \
+ officehelper.py \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/Package_python_shell.mk b/pyuno/Package_python_shell.mk
new file mode 100644
index 0000000000..f75cda39af
--- /dev/null
+++ b/pyuno/Package_python_shell.mk
@@ -0,0 +1,18 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Package_Package,python_shell,$(call gb_CustomTarget_get_workdir,pyuno/python_shell)))
+
+ifeq ($(OS),MACOSX)
+$(eval $(call gb_Package_add_file,python_shell,$(LIBO_ETC_FOLDER)/python,python.sh))
+else
+$(eval $(call gb_Package_add_file,python_shell,$(LIBO_BIN_FOLDER)/python,python.sh))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/Package_pyuno_pythonloader_ini.mk b/pyuno/Package_pyuno_pythonloader_ini.mk
new file mode 100644
index 0000000000..0fae146186
--- /dev/null
+++ b/pyuno/Package_pyuno_pythonloader_ini.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Package_Package,pyuno_pythonloader_ini,$(call gb_CustomTarget_get_workdir,pyuno/pythonloader_ini)))
+
+$(eval $(call gb_Package_add_files,pyuno_pythonloader_ini,$(LIBO_ETC_FOLDER), \
+ $(call gb_Helper_get_rcfile,pythonloader.uno) \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/PythonTest_pyuno_pytests_insertremovecells.mk b/pyuno/PythonTest_pyuno_pytests_insertremovecells.mk
new file mode 100644
index 0000000000..3b3e3c0988
--- /dev/null
+++ b/pyuno/PythonTest_pyuno_pytests_insertremovecells.mk
@@ -0,0 +1,20 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,pyuno_pytests_insertremovecells))
+
+$(eval $(call gb_PythonTest_set_defs,pyuno_pytests_insertremovecells,\
+ TDOC="$(SRCDIR)/pyuno/qa/pytests/testdocuments" \
+))
+
+$(eval $(call gb_PythonTest_add_modules,pyuno_pytests_insertremovecells,$(SRCDIR)/pyuno/qa/pytests,\
+ insertremovecells \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/PythonTest_pyuno_pytests_testcollections.mk b/pyuno/PythonTest_pyuno_pytests_testcollections.mk
new file mode 100644
index 0000000000..abd26699c2
--- /dev/null
+++ b/pyuno/PythonTest_pyuno_pytests_testcollections.mk
@@ -0,0 +1,27 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,pyuno_pytests_testcollections))
+
+$(eval $(call gb_PythonTest_add_modules,pyuno_pytests_testcollections,$(SRCDIR)/pyuno/qa/pytests,\
+ testcollections_XIndexAccess \
+ testcollections_XIndexReplace \
+ testcollections_XIndexContainer \
+ testcollections_XNameAccess \
+ testcollections_XNameReplace \
+ testcollections_XNameContainer \
+ testcollections_XEnumerationAccess \
+ testcollections_XEnumeration \
+ testcollections_XCellRange \
+ testcollections_mixednameindex \
+ testcollections_misc \
+ testcollections_misc2 \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/PythonTest_pyuno_pytests_testssl.mk b/pyuno/PythonTest_pyuno_pytests_testssl.mk
new file mode 100644
index 0000000000..e431af31a2
--- /dev/null
+++ b/pyuno/PythonTest_pyuno_pytests_testssl.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,pyuno_pytests_testssl))
+
+$(eval $(call gb_PythonTest_add_modules,pyuno_pytests_testssl,$(SRCDIR)/pyuno/qa/pytests,\
+ testssl \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/README.md b/pyuno/README.md
new file mode 100644
index 0000000000..95e4877067
--- /dev/null
+++ b/pyuno/README.md
@@ -0,0 +1,22 @@
+# Python UNO Bindings
+
+UNO bindings for the Python programming language.
+
+To have much joy debugging Python extensions you need to:
+
++ a) edit `pythonloader.py` in your install setting `DEBUG=1` at the top
++ b) `touch pyuno/source/module/pyuno_runtime.cxx` and `make debug=true` in `pyuno`
+
+Then you'll start to see your exceptions on the console instead of them getting
+lost at the UNO interface.
+
+Python also comes with a gdb script
+`libpython$(PYTHON_VERSION_MAJOR).$(PYTHON_VERSION_MINOR)m.so.1.0-gdb.py`
+that is copied to `instdir` and will be auto-loaded by `gdb`;
+it provides commands like `py-bt` to get a Python-level backtrace,
+and `py-print` to print Python variables.
+
+Another way to debug Python code is to use `pdb`: edit some initialization
+function to insert `import pdb; pdb.set_trace()` (somewhere so that it is
+executed early), then run `soffice` from a terminal and a command-line Python
+debugger will appear where you can set Python-level breakpoints.
diff --git a/pyuno/Rdb_pyuno.mk b/pyuno/Rdb_pyuno.mk
new file mode 100644
index 0000000000..f4e3fc7a79
--- /dev/null
+++ b/pyuno/Rdb_pyuno.mk
@@ -0,0 +1,12 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Rdb_Rdb_install,pyuno))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/demo/Addons.xcu b/pyuno/demo/Addons.xcu
new file mode 100644
index 0000000000..59d2d18563
--- /dev/null
+++ b/pyuno/demo/Addons.xcu
@@ -0,0 +1,36 @@
+<?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 .
+ -->
+
+<oor:node xmlns:oor="http://openoffice.org/2001/registry"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ oor:name="Addons" oor:package="org.openoffice.Office">
+<node oor:name="AddonUI">
+ <node oor:name="AddonMenu">
+ <node oor:name="org.openoffice.comp.pyuno.demo.HelloWorld" oor:op="replace">
+ <prop oor:name="URL" oor:type="xs:string">
+ <value>service:org.openoffice.comp.pyuno.demo.HelloWorld?insert</value>
+ </prop>
+ <prop oor:name="Title" oor:type="xs:string">
+ <value xml:lang="x-no-translate">Insert Hello World</value>
+ <value xml:lang="en-US">Insert Hello World</value>
+ </prop>
+ </node>
+ </node>
+</node>
+</oor:node>
diff --git a/pyuno/demo/biblioaccess.py b/pyuno/demo/biblioaccess.py
new file mode 100644
index 0000000000..0e5d0b4c80
--- /dev/null
+++ b/pyuno/demo/biblioaccess.py
@@ -0,0 +1,53 @@
+# -*- 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
+from com.sun.star.sdb.CommandType import COMMAND
+
+def main():
+ connectionString = "socket,host=localhost,port=2002"
+
+ url = "uno:" + connectionString + ";urp;StarOffice.ComponentContext"
+
+ localCtx = uno.getComponentContext()
+ localSmgr = localCtx.ServiceManager
+ resolver = localSmgr.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", localCtx)
+ ctx = resolver.resolve(url)
+ smgr = ctx.ServiceManager
+
+ rowset =smgr.createInstanceWithContext("com.sun.star.sdb.RowSet", ctx)
+ rowset.DataSourceName = "Bibliography"
+ rowset.CommandType = COMMAND
+ rowset.Command = "SELECT IDENTIFIER, AUTHOR FROM biblio"
+
+ rowset.execute();
+
+ print("Identifier\tAuthor")
+
+ id = rowset.findColumn("IDENTIFIER")
+ author = rowset.findColumn("AUTHOR")
+ while rowset.next():
+ print(rowset.getString(id) + "\t" + repr(rowset.getString(author)))
+
+ rowset.dispose();
+
+main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/hello_world_comp.py b/pyuno/demo/hello_world_comp.py
new file mode 100644
index 0000000000..020953b4f4
--- /dev/null
+++ b/pyuno/demo/hello_world_comp.py
@@ -0,0 +1,59 @@
+# -*- 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 unohelper
+
+from com.sun.star.task import XJobExecutor
+
+# implement a UNO component by deriving from the standard unohelper.Base class
+# and from the interface(s) you want to implement.
+class HelloWorldJob(unohelper.Base, XJobExecutor):
+ def __init__(self, ctx):
+ # store the component context for later use
+ self.ctx = ctx
+
+ def trigger(self, args):
+ # note: args[0] == "HelloWorld", see below config settings
+
+ # retrieve the desktop object
+ desktop = self.ctx.ServiceManager.createInstanceWithContext(
+ "com.sun.star.frame.Desktop", self.ctx)
+
+ # get current document model
+ model = desktop.getCurrentComponent()
+
+ # access the document's text property
+ text = model.Text
+
+ # create a cursor
+ cursor = text.createTextCursor()
+
+ # insert the text into the document
+ text.insertString(cursor, "Hello World", 0)
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+
+g_ImplementationHelper.addImplementation( \
+ HelloWorldJob, # UNO object class
+ "org.openoffice.comp.pyuno.demo.HelloWorld", # implementation name
+ ("com.sun.star.task.Job",),) # list of implemented services
+ # (the only service)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/makefile.mk b/pyuno/demo/makefile.mk
new file mode 100644
index 0000000000..11fe66f31b
--- /dev/null
+++ b/pyuno/demo/makefile.mk
@@ -0,0 +1,60 @@
+#
+# 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 .
+#
+
+PRJNAME=pyuno
+PRJ=..
+
+.INCLUDE : settings.mk
+.INCLUDE : pyversion.mk
+
+ROOT=$(MISC)/pyuno-doc
+
+FILES=\
+ $(ROOT)/python-bridge.html \
+ $(ROOT)/customized_setup.png \
+ $(ROOT)/mode_component.png \
+ $(ROOT)/mode_ipc.png \
+ $(ROOT)/modes.sxd \
+ $(ROOT)/optional_components.png \
+ $(ROOT)/samples/swriter.py \
+ $(ROOT)/samples/swritercomp.py \
+ $(ROOT)/samples/ooextract.py \
+ $(ROOT)/samples/biblioaccess.py \
+ $(ROOT)/samples/swritercompclient.py \
+ $(ROOT)/samples/hello_world_pyuno.zip
+
+
+$(MISC)/pyuno-doc.zip : dirs $(FILES)
+ -rm -f $@
+ cd $(MISC) && zip -r pyuno-doc.zip pyuno-doc
+
+dirs .PHONY :
+ -mkdir $(ROOT)
+ -mkdir $(ROOT)/samples
+
+$(ROOT)/samples/hello_world_pyuno.zip : hello_world_comp.py Addons.xcu
+ -rm -f $@
+ zip $@ hello_world_comp.py Addons.xcu
+
+$(ROOT)/samples/% : %
+ -rm -f $@
+ $(COPY) $? $@
+
+$(ROOT)/% : ../doc/%
+ -rm -f $@
+ $(COPY) $? $@
diff --git a/pyuno/demo/ooextract.py b/pyuno/demo/ooextract.py
new file mode 100644
index 0000000000..a5699548ce
--- /dev/null
+++ b/pyuno/demo/ooextract.py
@@ -0,0 +1,128 @@
+# -*- 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 getopt,sys
+import uno
+
+from unohelper import Base,systemPathToFileUrl
+from os import getcwd
+from com.sun.star.beans import PropertyValue
+from com.sun.star.uno import Exception as UnoException
+from com.sun.star.io import IOException, XOutputStream
+
+class OutputStream(Base, XOutputStream):
+ def __init__(self):
+ self.closed = 0
+
+ def closeOutput(self):
+ self.closed = 1
+
+ def writeBytes(self, seq):
+ sys.stdout.write(seq.value)
+
+ def flush(self):
+ pass
+
+def main():
+ retVal = 0
+ doc = None
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "hc:", ["help", "connection-string=", "html"])
+ format = None
+ url = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext"
+ filterName = "Text (Encoded)"
+ for o, a in opts:
+ if o in ("-h", "--help"):
+ usage()
+ sys.exit()
+ if o in ("-c", "--connection-string"):
+ url = "uno:" + a + ";urp;StarOffice.ComponentContext"
+ if o == "--html":
+ filterName = "HTML (StarWriter)"
+
+ print(filterName)
+ if not len(args):
+ usage()
+ sys.exit()
+
+ ctxLocal = uno.getComponentContext()
+ smgrLocal = ctxLocal.ServiceManager
+
+ resolver = smgrLocal.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", ctxLocal)
+ ctx = resolver.resolve(url)
+ smgr = ctx.ServiceManager
+
+ desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
+
+ cwd = systemPathToFileUrl(getcwd())
+ outProps = (
+ PropertyValue("FilterName" , 0, filterName, 0),
+ PropertyValue("OutputStream", 0, OutputStream(), 0))
+ inProps = PropertyValue("Hidden", 0 , True, 0),
+ for path in args:
+ try:
+ fileUrl = uno.absolutize(cwd, systemPathToFileUrl(path))
+ doc = desktop.loadComponentFromURL(fileUrl , "_blank", 0, inProps)
+
+ if not doc:
+ raise UnoException("Could not open stream for unknown reason", None)
+
+ doc.storeToURL("private:stream", outProps)
+ except IOException as e:
+ sys.stderr.write("Error during conversion: " + e.Message + "\n")
+ retVal = 1
+ except UnoException as e:
+ sys.stderr.write("Error (" + repr(e.__class__) + ") during conversion: " + e.Message + "\n")
+ retVal = 1
+ if doc:
+ doc.dispose()
+
+ except UnoException as e:
+ sys.stderr.write("Error (" + repr(e.__class__) + "): " + e.Message + "\n")
+ retVal = 1
+ except getopt.GetoptError as e:
+ sys.stderr.write(str(e) + "\n")
+ usage()
+ retVal = 1
+
+ sys.exit(retVal)
+
+def usage():
+ sys.stderr.write("usage: ooextract.py --help |\n"+
+ " [-c <connection-string> | --connection-string=<connection-string>\n"+
+ " file1 file2 ...\n"+
+ "\n" +
+ "Extracts plain text from documents and prints it to stdout.\n" +
+ "Requires an OpenOffice.org instance to be running. The script and the\n"+
+ "running OpenOffice.org instance must be able to access the file with\n"+
+ "by the same system path.\n"
+ "\n"+
+ "-c <connection-string> | --connection-string=<connection-string>\n" +
+ " The connection-string part of a UNO URL to where the\n" +
+ " the script should connect to in order to do the conversion.\n" +
+ " The strings defaults to socket,host=localhost,port=2002\n"
+ "--html \n"
+ " Instead of the text filter, the writer html filter is used\n"
+ )
+
+main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/pyunoenv.bat b/pyuno/demo/pyunoenv.bat
new file mode 100644
index 0000000000..b38c61e73d
--- /dev/null
+++ b/pyuno/demo/pyunoenv.bat
@@ -0,0 +1,23 @@
+rem
+rem This file is part of the LibreOffice project.
+rem
+rem This Source Code Form is subject to the terms of the Mozilla Public
+rem License, v. 2.0. If a copy of the MPL was not distributed with this
+rem file, You can obtain one at http://mozilla.org/MPL/2.0/.
+rem
+rem This file incorporates work covered by the following license notice:
+rem
+rem Licensed to the Apache Software Foundation (ASF) under one or more
+rem contributor license agreements. See the NOTICE file distributed
+rem with this work for additional information regarding copyright
+rem ownership. The ASF licenses this file to you under the Apache
+rem License, Version 2.0 (the "License"); you may not use this file
+rem except in compliance with the License. You may obtain a copy of
+rem the License at http://www.apache.org/licenses/LICENSE-2.0 .
+rem
+
+set OOOHOME=
+
+set PYTHONPATH=.;%OOOHOME%\program;%OOOHOME%\program\pydemo;%OOOHOME%\program\python-2.2.2;%PYTHONPATH%
+set PATH=%OOOHOME%\program;%PYTHONHOME%;%OOOHOME%\program\python-2.2.2\bin;%PATH%
+
diff --git a/pyuno/demo/pyunoenv.tcsh b/pyuno/demo/pyunoenv.tcsh
new file mode 100644
index 0000000000..653d72a380
--- /dev/null
+++ b/pyuno/demo/pyunoenv.tcsh
@@ -0,0 +1,49 @@
+#
+# 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 .
+#
+# the path to the office installation (e.g. /home/joe/OpenOffice.org1.1Beta)
+setenv OOOHOME /src4/OpenOffice.org1.1Beta2
+
+# don't modify anything beyond these lines
+#---------------------------------------------
+setenv PYTHONHOME $OOOHOME/program/python
+
+if( ! $?LD_LIBRARY_PATH ) then
+ setenv LD_LIBRARY_PATH
+endif
+
+if(! $?PYTHONPATH ) then
+ setenv PYTHONPATH
+endif
+
+if( ! $?LD_LIBRARY_PATH ) then
+setenv LD_LIBRARY_PATH
+endif
+
+if( "$PYTHONPATH" != "" ) then
+ setenv PYTHONPATH $OOOHOME/program:$OOOHOME/program/pydemo:$OOOHOME/program/python/lib:$PYTHONPATH
+else
+ setenv PYTHONPATH $OOOHOME/program:$OOOHOME/program/pydemo:$OOOHOME/program/python/lib
+endif
+
+setenv LD_LIBRARY_PATH $OOOHOME/program:$LD_LIBRARY_PATH
+
+if( $?PYTHONHOME ) then
+setenv PATH $PYTHONHOME/bin:$PATH
+endif
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/swriter.py b/pyuno/demo/swriter.py
new file mode 100644
index 0000000000..70af4f98d0
--- /dev/null
+++ b/pyuno/demo/swriter.py
@@ -0,0 +1,118 @@
+# -*- 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 .
+#
+
+# bootstrap uno component context
+import uno
+
+# a UNO struct later needed to create a document
+from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
+from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
+from com.sun.star.awt import Size
+
+def insertTextIntoCell( table, cellName, text, color ):
+ tableText = table.getCellByName( cellName )
+ cursor = tableText.createTextCursor()
+ cursor.setPropertyValue( "CharColor", color )
+ tableText.setString( text )
+
+localContext = uno.getComponentContext()
+
+resolver = localContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", localContext )
+
+smgr = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ServiceManager" )
+remoteContext = smgr.getPropertyValue( "DefaultContext" )
+
+#remoteContext = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
+#smgr = remoteContext.ServiceManager
+
+desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",remoteContext)
+
+# open a writer document
+doc = desktop.loadComponentFromURL( "private:factory/swriter","_blank", 0, () )
+
+text = doc.Text
+cursor = text.createTextCursor()
+text.insertString( cursor, "The first line in the newly created text document.\n", 0 )
+text.insertString( cursor, "Now we are in the second line\n" , 0 )
+
+# create a text table
+table = doc.createInstance( "com.sun.star.text.TextTable" )
+
+# with 4 rows and 4 columns
+table.initialize(4, 4)
+
+text.insertTextContent( cursor, table, 0 )
+rows = table.Rows
+
+table.setPropertyValue( "BackTransparent", False )
+table.setPropertyValue( "BackColor", 13421823 )
+row = rows[0]
+row.setPropertyValue( "BackTransparent", False )
+row.setPropertyValue( "BackColor", 6710932 )
+
+textColor = 16777215
+
+insertTextIntoCell( table, "A1", "FirstColumn", textColor )
+insertTextIntoCell( table, "B1", "SecondColumn", textColor )
+insertTextIntoCell( table, "C1", "ThirdColumn", textColor )
+insertTextIntoCell( table, "D1", "SUM", textColor )
+
+table.getCellByName("A2").setValue(22.5)
+table.getCellByName("B2").setValue(5615.3)
+table.getCellByName("C2").setValue(-2315.7)
+table.getCellByName("D2").setFormula("sum <A2:C2>")
+
+table.getCellByName("A3").setValue(21.5)
+table.getCellByName("B3").setValue(615.3)
+table.getCellByName("C3").setValue(-315.7)
+table.getCellByName("D3").setFormula("sum <A3:C3>")
+
+table.getCellByName("A4").setValue(121.5)
+table.getCellByName("B4").setValue(-615.3)
+table.getCellByName("C4").setValue(415.7)
+table.getCellByName("D4").setFormula("sum <A4:C4>")
+
+
+cursor.setPropertyValue( "CharColor", 255 )
+cursor.setPropertyValue( "CharShadowed", True )
+
+text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+text.insertString( cursor, " This is a colored Text - blue with shadow\n" , 0 )
+text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+
+textFrame = doc.createInstance( "com.sun.star.text.TextFrame" )
+textFrame.setSize( Size(15000,400))
+textFrame.setPropertyValue( "AnchorType" , AS_CHARACTER )
+
+
+text.insertTextContent( cursor, textFrame, 0 )
+
+textInTextFrame = textFrame.getText()
+cursorInTextFrame = textInTextFrame.createTextCursor()
+textInTextFrame.insertString( cursorInTextFrame, "The first line in the newly created text frame.", 0 )
+textInTextFrame.insertString( cursorInTextFrame, "\nWith this second line the height of the rame raises.",0)
+text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+
+cursor.setPropertyValue( "CharColor", 65536 )
+cursor.setPropertyValue( "CharShadowed", False )
+
+text.insertString( cursor, " That's all for now!" , 0 )
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/swritercomp.py b/pyuno/demo/swritercomp.py
new file mode 100644
index 0000000000..7c200cc7be
--- /dev/null
+++ b/pyuno/demo/swritercomp.py
@@ -0,0 +1,128 @@
+# -*- 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 .
+#
+
+# just a simple copy of the swriter.py demo, but implemented as a component. The advantage is,
+# that the component may run within the office process which may give a performance improvement.
+
+import unohelper
+import uno
+
+# a UNO struct later needed to create a document
+from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
+from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
+from com.sun.star.awt import Size
+
+from com.sun.star.lang import XMain
+
+def insertTextIntoCell( table, cellName, text, color ):
+ tableText = table.getCellByName( cellName )
+ cursor = tableText.createTextCursor()
+ cursor.setPropertyValue( "CharColor", color )
+ tableText.setString( text )
+
+# the UNO component
+# implementing the interface com.sun.star.lang.XMain
+# unohelper.Base implements the XTypeProvider interface
+class SWriterComp(XMain,unohelper.Base):
+ def __init__( self, ctx ):
+ self.ctx = ctx
+
+ # implementation for XMain.run( [in] sequence< any > )
+ def run( self,args ):
+ ctx = self.ctx
+ smgr = ctx.ServiceManager
+ desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
+
+ # open a writer document
+ doc = desktop.loadComponentFromURL( "private:factory/swriter","_blank", 0, () )
+
+ text = doc.Text
+ cursor = text.createTextCursor()
+ text.insertString( cursor, "The first line in the newly created text document.\n", 0 )
+ text.insertString( cursor, "Now we are in the second line\n" , 0 )
+
+ # create a text table
+ table = doc.createInstance( "com.sun.star.text.TextTable" )
+
+ # with 4 rows and 4 columns
+ table.initialize( 4,4)
+
+ text.insertTextContent( cursor, table, 0 )
+ rows = table.Rows
+
+ table.setPropertyValue( "BackTransparent", uno.Bool(0) )
+ table.setPropertyValue( "BackColor", 13421823 )
+ row = rows[0]
+ row.setPropertyValue( "BackTransparent", uno.Bool(0) )
+ row.setPropertyValue( "BackColor", 6710932 )
+
+ textColor = 16777215
+
+ insertTextIntoCell( table, "A1", "FirstColumn", textColor )
+ insertTextIntoCell( table, "B1", "SecondColumn", textColor )
+ insertTextIntoCell( table, "C1", "ThirdColumn", textColor )
+ insertTextIntoCell( table, "D1", "SUM", textColor )
+
+ table.getCellByName("A2").setValue(22.5)
+ table.getCellByName("B2").setValue(5615.3)
+ table.getCellByName("C2").setValue(-2315.7)
+ table.getCellByName("D2").setFormula("sum <A2:C2>")
+
+ table.getCellByName("A3").setValue(21.5)
+ table.getCellByName("B3").setValue(615.3)
+ table.getCellByName("C3").setValue(-315.7)
+ table.getCellByName("D3").setFormula("sum <A3:C3>")
+
+ table.getCellByName("A4").setValue(121.5)
+ table.getCellByName("B4").setValue(-615.3)
+ table.getCellByName("C4").setValue(415.7)
+ table.getCellByName("D4").setFormula("sum <A4:C4>")
+
+
+ cursor.setPropertyValue( "CharColor", 255 )
+ cursor.setPropertyValue( "CharShadowed", uno.Bool(1) )
+
+ text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+ text.insertString( cursor, " This is a colored Text - blue with shadow\n" , 0 )
+ text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+
+ textFrame = doc.createInstance( "com.sun.star.text.TextFrame" )
+ textFrame.setSize( Size(15000,400))
+ textFrame.setPropertyValue( "AnchorType" , AS_CHARACTER )
+
+ text.insertTextContent( cursor, textFrame, 0 )
+
+ textInTextFrame = textFrame.getText()
+ cursorInTextFrame = textInTextFrame.createTextCursor()
+ textInTextFrame.insertString( cursorInTextFrame, "The first line in the newly created text frame.", 0 )
+ textInTextFrame.insertString( cursorInTextFrame, "\nWith this second line the height of the rame raises.",0)
+ text.insertControlCharacter( cursor, PARAGRAPH_BREAK, 0 )
+
+ cursor.setPropertyValue( "CharColor", 65536 )
+ cursor.setPropertyValue( "CharShadowed", uno.Bool(0) )
+
+ text.insertString( cursor, " That's all for now!" , 0 )
+ return 0
+
+# pythonloader looks for a static g_ImplementationHelper variable
+g_ImplementationHelper = unohelper.ImplementationHelper()
+g_ImplementationHelper.addImplementation( \
+ SWriterComp,"org.openoffice.comp.pyuno.swriter",("org.openoffice.demo.SWriter",),)
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/demo/swritercompclient.py b/pyuno/demo/swritercompclient.py
new file mode 100644
index 0000000000..c4d6cdbf6a
--- /dev/null
+++ b/pyuno/demo/swritercompclient.py
@@ -0,0 +1,32 @@
+# -*- 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
+
+localContext = uno.getComponentContext()
+resolver = localContext.ServiceManager.createInstanceWithContext(
+ "com.sun.star.bridge.UnoUrlResolver", localContext )
+remoteContext = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
+remoteSmgr = remoteContext.ServiceManager
+
+pyComp = remoteSmgr.createInstanceWithContext( "org.openoffice.demo.SWriter" , remoteContext )
+
+pyComp.run( (), )
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/doc/modes.sxd b/pyuno/doc/modes.sxd
new file mode 100644
index 0000000000..848912b923
--- /dev/null
+++ b/pyuno/doc/modes.sxd
Binary files differ
diff --git a/pyuno/inc/pyuno.hxx b/pyuno/inc/pyuno.hxx
new file mode 100644
index 0000000000..f7cab36c73
--- /dev/null
+++ b/pyuno/inc/pyuno.hxx
@@ -0,0 +1,321 @@
+/* -*- 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
+
+#ifndef Py_PYTHON_H
+#include <Python.h>
+#endif // #ifdef Py_PYTHON_H
+
+#include <com/sun/star/uno/Any.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+namespace com::sun::star::uno { template <typename > class Reference; }
+
+/**
+ External interface of the Python UNO bridge.
+
+ This is a C++ interface, because the core UNO components
+ invocation and proxyfactory are used to implement the bridge.
+
+ This interface is somewhat private and my change in future.
+
+ A scripting framework implementation may use this interface
+ to do the necessary conversions.
+*/
+
+#if defined LO_DLLIMPLEMENTATION_PYUNO
+#define LO_DLLPUBLIC_PYUNO SAL_DLLPUBLIC_EXPORT
+#else
+#define LO_DLLPUBLIC_PYUNO SAL_DLLPUBLIC_IMPORT
+#endif
+
+/** function called by the python runtime to initialize the
+ pyuno module.
+
+ preconditions: python has been initialized before and
+ the global interpreter lock is held
+*/
+
+extern "C" LO_DLLPUBLIC_PYUNO PyObject* PyInit_pyuno();
+
+namespace pyuno
+{
+
+enum NotNull
+{
+ /** definition of a no acquire enum for ctors
+ */
+ NOT_NULL
+};
+
+/** Helper class for keeping references to python objects.
+ BEWARE: Look up every python function you use to check
+ whether you get an acquired or not acquired object pointer
+ (python terminus for a not acquired object pointer
+ is 'borrowed reference'). Use in the acquired pointer cases the
+ PyRef( pointer, SAL_NO_ACQUIRE) ctor.
+
+ precondition: python has been initialized before and
+ the global interpreter lock is held
+
+*/
+class PyRef
+{
+ PyObject *m;
+public:
+ PyRef () : m(nullptr) {}
+ PyRef( PyObject * p ) : m( p ) { Py_XINCREF( m ); }
+
+ PyRef( PyObject * p, __sal_NoAcquire ) : m( p ) {}
+
+ PyRef( PyObject * p, __sal_NoAcquire, NotNull ) : m( p )
+ {
+ if (!m)
+ throw std::bad_alloc();
+ }
+
+ PyRef(const PyRef &r) : m(r.get()) { Py_XINCREF(m); }
+ PyRef(PyRef &&r) noexcept : m(r.get()) { r.scratch(); }
+
+ ~PyRef() { Py_XDECREF( m ); }
+
+ PyObject *get() const noexcept { return m; }
+
+ PyObject * getAcquired() const
+ {
+ Py_XINCREF( m );
+ return m;
+ }
+
+ PyRef& operator=(const PyRef& r)
+ {
+ PyObject *tmp = m;
+ m = r.getAcquired();
+ Py_XDECREF(tmp);
+ return *this;
+ }
+
+ PyRef& operator=(PyRef&& r)
+ {
+ PyObject *tmp = m;
+ m = r.get();
+ r.scratch();
+ Py_XDECREF(tmp);
+ return *this;
+ }
+
+ bool operator == ( const PyRef & r ) const
+ {
+ return r.get() == m;
+ }
+
+ /** clears the reference without decreasing the reference count
+ only seldom needed ! */
+ void scratch() noexcept
+ {
+ m = nullptr;
+ }
+
+ /** returns 1 when the reference points to a python object python object,
+ otherwise 0.
+ */
+ bool is() const
+ {
+ return m != nullptr;
+ }
+
+ struct Hash
+ {
+ sal_IntPtr operator () ( const PyRef &r) const { return reinterpret_cast<sal_IntPtr>( r.get() ); }
+ };
+};
+
+//struct stRuntimeImpl;
+typedef struct stRuntimeImpl RuntimeImpl;
+
+enum ConversionMode { ACCEPT_UNO_ANY, REJECT_UNO_ANY };
+
+
+/** The pyuno::Runtime class keeps the internal state of the python UNO bridge
+ for the currently in use python interpreter.
+
+ You may keep a Runtime instance, use it from a different thread, etc. But you must
+ make sure to fulfill all preconditions mentioned for the specific methods.
+*/
+
+class LO_DLLPUBLIC_PYUNO Runtime
+{
+ RuntimeImpl *impl;
+
+ /**
+ Safely unpacks a Python iterator into a sequence, then
+ stores it in an Any. Used internally by pyObject2Any
+ */
+ bool pyIterUnpack( PyObject *const, css::uno::Any & ) const;
+public:
+ ~Runtime( );
+
+ /**
+ preconditions: python has been initialized before,
+ the global interpreter lock is held and pyuno
+ has been initialized for the currently used interpreter.
+
+ Note: This method exists for efficiency reasons to save
+ lookup costs for any2PyObject and pyObject2Any
+
+ @throw RuntimeException in case the runtime has not been
+ initialized before
+ */
+ Runtime();
+
+ Runtime( const Runtime & );
+ Runtime & operator = ( const Runtime & );
+
+ /** Initializes the python-UNO bridge. May be called only once per python interpreter.
+
+ @param ctx the component context is used to instantiate bridge services needed
+ for bridging such as invocation, typeconverter, invocationadapterfactory, etc.
+
+ preconditions: python has been initialized before and
+ the global interpreter lock is held and pyuno is not
+ initialized (see isInitialized() ).
+
+ @throw RuntimeException in case the thread is not attached or the runtime
+ has not been initialized.
+ */
+ static void initialize(
+ const css::uno::Reference< css::uno::XComponentContext > & ctx );
+
+ /** Checks, whether the uno runtime is already initialized in the current python interpreter.
+
+ @throws css::uno::RuntimeException
+ */
+ static bool isInitialized();
+
+ /** converts something contained in a UNO Any to a Python object
+
+ preconditions: python has been initialized before,
+ the global interpreter lock is held and pyuno::Runtime
+ has been initialized.
+
+ @throws css::script::CannotConvertException
+ @throws css::lang::IllegalArgumentException
+ @throws css::uno::RuntimeException
+ */
+ PyRef any2PyObject (const css::uno::Any &source ) const;
+
+ /** converts a Python object to a UNO any
+
+ preconditions: python has been initialized before,
+ the global interpreter lock is held and pyuno
+ has been initialized
+
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Any pyObject2Any (
+ const PyRef & source , enum ConversionMode mode = REJECT_UNO_ANY ) const;
+
+ /** extracts a proper uno exception from a given python exception
+ */
+ css::uno::Any extractUnoException(
+ const PyRef & excType, const PyRef & excValue, const PyRef & excTraceback) const;
+
+ /** Returns the internal handle. Should only be used by the module implementation
+ */
+ RuntimeImpl *getImpl() const { return impl; }
+};
+
+
+/** helper class for attaching the current thread to the python runtime.
+
+ Attaching is done creating a new threadstate for the given interpreter
+ and acquiring the global interpreter lock.
+
+ Usage:
+
+ ... don't use python here
+ {
+ PyThreadAttach guard( PyInterpreterState_Head() );
+ {
+ ... do whatever python code you want
+ {
+ PyThreadDetach antiguard;
+ ... don't use python here
+ }
+ ... do whatever python code you want
+ }
+ }
+ ... don't use python here
+
+ Note: The additional scope brackets after the PyThreadAttach are needed,
+ e.g. when you would leave them away, dtors of potential pyrefs
+ may be called after the thread has detached again.
+ */
+class LO_DLLPUBLIC_PYUNO PyThreadAttach
+{
+ PyThreadState *tstate;
+ bool m_isNewState;
+ PyThreadAttach ( const PyThreadAttach & ) = delete;
+ PyThreadAttach & operator = ( const PyThreadAttach & ) = delete;
+public:
+
+ /** Creates a new python threadstate and acquires the global interpreter lock.
+ precondition: The current thread MUST NOT hold the global interpreter lock.
+ postcondition: The global interpreter lock is acquired
+
+ @throws css::uno::RuntimeException
+ in case no pythread state could be created
+ */
+ PyThreadAttach( PyInterpreterState *interp);
+
+
+ /** Releases the global interpreter lock and destroys the thread state.
+ */
+ ~PyThreadAttach();
+};
+
+/** helper class for detaching the current thread from the python runtime
+ to do some blocking, non-python related operation.
+
+ @see PyThreadAttach
+*/
+class PyThreadDetach
+{
+ PyThreadState *tstate;
+ PyThreadDetach ( const PyThreadDetach & ) = delete;
+ PyThreadDetach & operator = ( const PyThreadDetach & ) = delete;
+
+public:
+ /** Releases the global interpreter lock.
+
+ precondition: The current thread MUST hold the global interpreter lock.
+ postcondition: The current thread does not hold the global interpreter lock anymore.
+
+ @throws css::uno::RuntimeException
+ */
+ PyThreadDetach();
+ /** Acquires the global interpreter lock again
+ */
+ ~PyThreadDetach();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/qa/pytests/insertremovecells.py b/pyuno/qa/pytests/insertremovecells.py
new file mode 100644
index 0000000000..2583fdb134
--- /dev/null
+++ b/pyuno/qa/pytests/insertremovecells.py
@@ -0,0 +1,77 @@
+import pathlib
+import unittest
+
+from org.libreoffice.unotest import pyuno, mkPropertyValue, makeCopyFromTDOC
+
+
+class InsertRemoveCells(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.xContext = pyuno.getComponentContext()
+ pyuno.private_initTestEnvironment()
+
+ def test_fdo74824_load(self):
+ ctxt = self.xContext
+ assert(ctxt)
+
+ smgr = ctxt.ServiceManager
+ desktop = smgr.createInstanceWithContext('com.sun.star.frame.Desktop', ctxt)
+ load_props = tuple(mkPropertyValue(k, v) for (k, v) in (
+ ('Hidden', True),
+ ('ReadOnly', False)
+ ))
+ tdoc_path = makeCopyFromTDOC('fdo74824.ods')
+ url = pathlib.Path(tdoc_path).as_uri()
+ doc = desktop.loadComponentFromURL(url, "_blank", 0, load_props)
+
+ sheet = doc.Sheets.Sheet1
+ area = sheet.getCellRangeByName('A2:B4')
+ addr = area.getRangeAddress()
+
+ # 2 = intended to shift cells right, but I don't know where to find
+ # the ENUM to put in its place. Corrections welcome.
+ sheet.insertCells(addr, 2)
+
+ # basically, the insertCells call is the test: it should not crash
+ # LibreOffice. However, for completeness, we should test the cell
+ # contents as well.
+
+ empty_cells = (
+ (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3),
+ (3, 1), (4, 0), (4, 2), (5, 0), (5, 2), (5, 3),
+ )
+ formula_cells = (
+ (2, 0, '=(1+GDR)^-D1', '1.000', 1.0),
+ (4, 1, '=(1+GDR)^-F2', '0.125', 0.125),
+ (4, 3, '=SUM(C1:C2)', '1.000', 1.0),
+ )
+ value_cells = (
+ (2, 2, '2010', 2010.0),
+ (2, 3, '7', 7.0),
+ (3, 0, '0', 0),
+ (3, 2, '2012', 2012.0),
+ (3, 3, '6', 6.0),
+ (5, 1, '1', 1.0),
+ )
+ for col, row in empty_cells:
+ cell = sheet[row,col]
+ self.assertEqual('EMPTY', cell.Type.value)
+
+ for col, row, f, s, val in formula_cells:
+ cell = sheet[row,col]
+ self.assertEqual('FORMULA', cell.Type.value)
+ self.assertEqual(f, cell.getFormula())
+ self.assertEqual(s, cell.String)
+ self.assertEqual(val, cell.Value)
+
+ for col, row, s, val in value_cells:
+ cell = sheet[row,col]
+ self.assertEqual(s, cell.String)
+ self.assertEqual(val, cell.Value)
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pyuno/qa/pytests/testcollections_XCellRange.py b/pyuno/qa/pytests/testcollections_XCellRange.py
new file mode 100644
index 0000000000..6ab827ab33
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XCellRange.py
@@ -0,0 +1,399 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.table import CellAddress
+
+# TextTable instance factory
+def getTextTableInstance(doc):
+ return doc.createInstance('com.sun.star.text.TextTable')
+
+# Tests behaviour of objects implementing XCellRange using the new-style
+# collection accessors
+
+class TestXCellRange(CollectionsTestBase):
+
+ # TODO negative indices
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Spreadsheet
+ # Cell at Row 0, Col 0
+ def test_XCellRange_Spreadsheet_Cell_00(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ cell = sht[0, 0]
+
+ # Then
+ self.assertEqual(0, cell.CellAddress.Sheet)
+ self.assertEqual(0, cell.CellAddress.Row)
+ self.assertEqual(0, cell.CellAddress.Column)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Text table
+ # Cell at Row 0, Col 0
+ def test_XCellRange_Table_Cell_00(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+
+ # When
+ cell = tbl[0, 0]
+
+ # Then
+ self.assertEqual('A1', cell.CellName)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Spreadsheet
+ # Cell at Row 3, Col 7
+ def test_XCellRange_Spreadsheet_Cell_37(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[3, 7]
+
+ # Then
+ self.assertEqual(0, rng.CellAddress.Sheet)
+ self.assertEqual(3, rng.CellAddress.Row)
+ self.assertEqual(7, rng.CellAddress.Column)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Text table
+ # Cell at Row 3, Col 7
+ def test_XCellRange_Table_Cell_37(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+
+ # When
+ cell = tbl[3, 7]
+
+ # Then
+ self.assertEqual('H4', cell.CellName)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0,1:2] # Access cell range by index,slice
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Index_Slice(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[0, 1:3]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(1, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(2, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0,1:2] # Access cell range by index,slice
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Index_Slice(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x, 10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[0, 1:3]
+
+ # Then
+ self.assertEqual((('101', '102'),), rng.DataArray)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[1:2,0] # Access cell range by slice,index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Slice_Index(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[1:3, 0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(2, rng.RangeAddress.EndRow)
+ self.assertEqual(0, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[1:2,0] # Access cell range by index,slice
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Slice_Index(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x, 10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[1:3, 0]
+
+ # Then
+ self.assertEqual((('110',), ('120',)), rng.DataArray)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Slices(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[1:3, 3:5]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(3, rng.RangeAddress.StartColumn)
+ self.assertEqual(2, rng.RangeAddress.EndRow)
+ self.assertEqual(4, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Spreadsheet
+ # Zero rows/columns
+ def test_XCellRange_Spreadsheet_Range_Slices_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ rng = sht[1:1, 3:5]
+ with self.assertRaises(KeyError):
+ rng = sht[1:3, 3:3]
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Slices(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x, 10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[1:3, 3:5]
+
+ # Then
+ self.assertEqual((('113', '114'), ('123', '124')), rng.DataArray)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # rng = cellrange['A1:B2'] # Access cell range by descriptor
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Descriptor(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht['A3:B4']
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(2, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(3, rng.RangeAddress.EndRow)
+ self.assertEqual(1, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange['A1:B2'] # Access cell range by descriptor
+ # For:
+ # Table
+ def test_XCellRange_Table_Range_Descriptor(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = getTextTableInstance(doc)
+ text_table.initialize(10, 10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+ tbl = doc.TextTables[0]
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x, 10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl['A3:B4']
+
+ # Then
+ self.assertEqual((('120', '121'), ('130', '131')), rng.DataArray)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # rng = cellrange['Name'] # Access cell range by name
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Name(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+ expr = '$' + sht.Name + '.$C2:F10'
+ addr = CellAddress(Sheet=0, Row=1, Column=2)
+ sht.NamedRanges.addNewByName('foo', expr, addr, 0)
+
+ # When
+ rng = sht['foo']
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(2, rng.RangeAddress.StartColumn)
+ self.assertEqual(9, rng.RangeAddress.EndRow)
+ self.assertEqual(5, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0] # Access cell range by row index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_RowIndex(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(16383, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[0,:] # Access cell range by row index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_RowIndex_FullSlice(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[0, :]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(16383, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # rng = cellrange[:,0] # Access cell range by column index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_FullSlice_ColumnIndex(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets[0]
+
+ # When
+ rng = sht[:, 0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(1048575, rng.RangeAddress.EndRow)
+ self.assertEqual(0, rng.RangeAddress.EndColumn)
+
+ spr.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XEnumeration.py b/pyuno/qa/pytests/testcollections_XEnumeration.py
new file mode 100644
index 0000000000..a12d303596
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XEnumeration.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# Tests behaviour of objects implementing XEnumeration using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXEnumeration(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in itr: ... # Iteration of named iterator
+ # For:
+ # 1 element
+ def test_XEnumeration_ForIn(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraphs = []
+ itr = iter(doc.Text.createEnumeration())
+ for para in itr:
+ paragraphs.append(para)
+
+ # Then
+ self.assertEqual(1, len(paragraphs))
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Present value
+ def test_XEnumeration_IfIn_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc.Text.createEnumeration().nextElement()
+ itr = iter(doc.Text.createEnumeration())
+ result = paragraph in itr
+
+ # Then
+ self.assertTrue(result)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Absent value
+ def test_XEnumeration_IfIn_Absent(self):
+ # Given
+ doc1 = self.createBlankTextDocument()
+ doc2 = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc2.Text.createEnumeration().nextElement()
+ itr = iter(doc1.Text.createEnumeration())
+ result = paragraph in itr
+
+ # Then
+ self.assertFalse(result)
+
+ doc1.close(True)
+ doc2.close(True)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # None
+ def test_XEnumeration_IfIn_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text.createEnumeration())
+ result = None in itr
+
+ # Then
+ self.assertFalse(result)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Invalid value (string)
+ # Note: Ideally this would raise TypeError in the same manner as for
+ # XEnumerationAccess, but an XEnumeration doesn't know the type of its
+ # values
+ def test_XEnumeration_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text.createEnumeration())
+ result = 'foo' in itr
+
+ # Then
+ self.assertFalse(result)
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XEnumerationAccess.py b/pyuno/qa/pytests/testcollections_XEnumerationAccess.py
new file mode 100644
index 0000000000..385514ae77
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XEnumerationAccess.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# Tests behaviour of objects implementing XEnumerationAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXEnumerationAccess(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator
+ # For:
+ # 1 element
+ def test_XEnumerationAccess_ForIn(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraphs = []
+ for para in doc.Text:
+ paragraphs.append(para)
+
+ # Then
+ self.assertEqual(1, len(paragraphs))
+
+ doc.close(True)
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator
+ # For:
+ # 1 element
+ def test_XEnumerationAccess_Iter(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Present value
+ def test_XEnumerationAccess_IfIn_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc.Text.createEnumeration().nextElement()
+ result = paragraph in doc.Text
+
+ # Then
+ self.assertTrue(result)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent value
+ def test_XEnumerationAccess_IfIn_Absent(self):
+ # Given
+ doc1 = self.createBlankTextDocument()
+ doc2 = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc2.Text.createEnumeration().nextElement()
+ result = paragraph in doc1.Text
+
+ # Then
+ self.assertFalse(result)
+
+ doc1.close(True)
+ doc2.close(True)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # None
+ def test_XEnumerationAccess_IfIn_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ result = None in doc.Text
+
+ # Then
+ self.assertFalse(result)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Invalid value (string)
+ def test_XEnumerationAccess_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ result = 'foo' in doc.Text
+
+ # Then
+ self.assertFalse(result)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Invalid value (dict)
+ def test_XEnumerationAccess_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ result = {} in doc.Text
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XIndexAccess.py b/pyuno/qa/pytests/testcollections_XIndexAccess.py
new file mode 100644
index 0000000000..ee668b7bfb
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexAccess.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+#
+# 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
+
+from inspect import isclass
+from testcollections_base import CollectionsTestBase
+
+
+# Footnote instance factory
+def getFootnoteInstance(doc):
+ return doc.createInstance("com.sun.star.text.Footnote")
+
+# Tests behaviour of objects implementing XIndexAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexAccess(CollectionsTestBase):
+
+ def insertTestFootnotes(self, doc, count):
+ cursor = doc.Text.createTextCursor()
+ for i in range(count):
+ footnote = getFootnoteInstance(doc)
+ footnote.Label = 'n'+str(i)
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ def readValuesTestFixture(self, doc, count, key, expected):
+ # Given
+ doc.Text.setString('')
+ self.insertTestFootnotes(doc, count)
+
+ # When
+ captured = None
+ try:
+ actual = doc.Footnotes[key]
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isclass(expected) and issubclass(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(expected.__name__, type(captured).__name__)
+ elif type(expected) is tuple:
+ # expected is tuple
+ self.assertEqual(None, captured)
+ self.assertTrue(type(actual) is tuple)
+ self.assertEqual(len(expected), len(actual))
+ for i in range(len(expected)):
+ self.assertEqual('n'+str(expected[i]), actual[i].Label)
+ else:
+ # expected is value
+ self.assertEqual(None, captured)
+ self.assertTrue(type(actual) is not tuple)
+ self.assertEqual('n'+str(expected), actual.Label)
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # length = 0
+ def test_XIndexAccess_Len_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ count = len(doc.Footnotes)
+
+ # Then
+ self.assertEqual(0, count)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # length = 1
+ def test_XIndexAccess_Len_1(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = getFootnoteInstance(doc)
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ count = len(doc.Footnotes)
+
+ # Then
+ self.assertEqual(1, count)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # val = obj[0] # Access by index
+ # For:
+ # Single indices
+ def test_XIndexAccess_ReadIndex_Single(self):
+ doc = self.createBlankTextDocument()
+ self.readValuesTestFixture(doc, 0, -1, IndexError)
+ self.readValuesTestFixture(doc, 0, 0, IndexError)
+ self.readValuesTestFixture(doc, 0, 1, IndexError)
+ self.readValuesTestFixture(doc, 1, -3, IndexError)
+ self.readValuesTestFixture(doc, 1, -2, IndexError)
+ self.readValuesTestFixture(doc, 1, -1, 0)
+ self.readValuesTestFixture(doc, 1, 0, 0)
+ self.readValuesTestFixture(doc, 1, 1, IndexError)
+ self.readValuesTestFixture(doc, 1, 2, IndexError)
+ self.readValuesTestFixture(doc, 2, -4, IndexError)
+ self.readValuesTestFixture(doc, 2, -3, IndexError)
+ self.readValuesTestFixture(doc, 2, -2, 0)
+ self.readValuesTestFixture(doc, 2, -1, 1)
+ self.readValuesTestFixture(doc, 2, 0, 0)
+ self.readValuesTestFixture(doc, 2, 1, 1)
+ self.readValuesTestFixture(doc, 2, 2, IndexError)
+ self.readValuesTestFixture(doc, 2, 3, IndexError)
+ doc.close(True);
+
+ def test_XIndexAccess_ReadIndex_Single_Invalid(self):
+ doc = self.createBlankTextDocument()
+ self.readValuesTestFixture(doc, 0, None, TypeError)
+ self.readValuesTestFixture(doc, 0, 'foo', TypeError)
+ self.readValuesTestFixture(doc, 0, 12.34, TypeError)
+ self.readValuesTestFixture(doc, 0, (0, 1), TypeError)
+ self.readValuesTestFixture(doc, 0, [0, 1], TypeError)
+ self.readValuesTestFixture(doc, 0, {'a': 'b'}, TypeError)
+ doc.close(True);
+
+ # Tests syntax:
+ # val1,val2 = obj[2:4] # Access by slice
+ def test_XIndexAccess_ReadSlice(self):
+ doc = self.createBlankTextDocument()
+ test_max = 4
+ for i in range(test_max):
+ t = tuple(range(i))
+ for j in [x for x in range(-test_max-2, test_max+3)] + [None]:
+ for k in [x for x in range(-test_max-2, test_max+3)] + [None]:
+ key = slice(j, k)
+ expected = t[key]
+ self.readValuesTestFixture(doc, i, key, expected)
+ doc.close(True);
+
+ # Tests syntax:
+ # val1,val2 = obj[0:3:2] # Access by extended slice
+ def test_XIndexAccess_ReadExtendedSlice(self):
+ doc = self.createBlankTextDocument()
+ test_max = 4
+ for i in range(test_max):
+ t = tuple(range(i))
+ for j in [x for x in range(-test_max-2, test_max+3)] + [None]:
+ for k in [x for x in range(-test_max-2, test_max+3)] + [None]:
+ for l in [-2, -1, 2]:
+ key = slice(j, k, l)
+ expected = t[key]
+ self.readValuesTestFixture(doc, i, key, expected)
+ doc.close(True);
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Present element
+ def test_XIndexAccess_In_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = getFootnoteInstance(doc)
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+ footnote = doc.Footnotes[0]
+
+ # When
+ present = footnote in doc.Footnotes
+
+ # Then
+ self.assertTrue(present)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # None
+ def test_XIndexAccess_In_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ present = None in doc.Footnotes
+
+ # Then
+ self.assertFalse(present)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent element (string)
+ def test_XIndexAccess_In_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ present = "foo" in doc.Footnotes
+
+ # Then
+ self.assertFalse(present)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent element (dict)
+ def test_XIndexAccess_In_Dict(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ present = {} in doc.Footnotes
+
+ doc.close(True);
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 0 elements
+ def test_XIndexAccess_ForIn_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ read_footnotes = []
+ for f in doc.Footnotes:
+ read_footnotes.append(f)
+
+ # Then
+ self.assertEqual(0, len(read_footnotes))
+
+ doc.close(True);
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 1 element
+ def test_XIndexAccess_ForIn_1(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = getFootnoteInstance(doc)
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ read_footnotes = []
+ for f in doc.Footnotes:
+ read_footnotes.append(f)
+
+ # Then
+ self.assertEqual(1, len(read_footnotes))
+ self.assertEqual('foo', read_footnotes[0].Label)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 2 elements
+ def test_XIndexAccess_ForIn_2(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote1 = getFootnoteInstance(doc)
+ footnote2 = getFootnoteInstance(doc)
+ footnote1.setLabel('foo')
+ footnote2.setLabel('bar')
+ doc.Text.insertTextContent(cursor, footnote1, 0)
+ doc.Text.insertTextContent(cursor, footnote2, 0)
+
+ # When
+ read_footnotes = []
+ for f in doc.Footnotes:
+ read_footnotes.append(f)
+
+ # Then
+ self.assertEqual(2, len(read_footnotes))
+ self.assertEqual('foo', read_footnotes[0].Label)
+ self.assertEqual('bar', read_footnotes[1].Label)
+
+ doc.close(True);
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator (values)
+ # For:
+ # 1 element
+ def test_XIndexAccess_Iter_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = getFootnoteInstance(doc)
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ itr = iter(doc.Footnotes)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+ doc.close(True);
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XIndexContainer.py b/pyuno/qa/pytests/testcollections_XIndexContainer.py
new file mode 100644
index 0000000000..1df5f07740
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexContainer.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+#
+# 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 uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+# DataForm factory
+def getDataFormInstance(doc):
+ return doc.createInstance("com.sun.star.form.component.DataForm")
+
+
+# Tests behaviour of objects implementing XIndexContainer using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexContainer(CollectionsTestBase):
+
+ def generateTestPropertyValues(self, count):
+ sm = self.context.ServiceManager
+ values = sm.createInstanceWithContext("com.sun.star.document.IndexedPropertyValues", self.context)
+ for i in range(count):
+ properties = (PropertyValue(Name='n'+str(i), Value='v'+str(i)),)
+ uno.invoke(values, "insertByIndex", (i, uno.Any("[]com.sun.star.beans.PropertyValue", properties)))
+ return values
+
+ def generateTestTuple(self, values):
+ properties = []
+ for i in values:
+ properties.append((PropertyValue(Name='n'+str(i), Value='v'+str(i)),))
+ return tuple(properties)
+
+ def assignValuesTestFixture(self, count, key, values, expected):
+ # Given
+ property_values = self.generateTestPropertyValues(count)
+ if type(values) is list:
+ to_assign = self.generateTestTuple(values)
+ else:
+ to_assign = values
+ if not (isinstance(expected, Exception)):
+ to_compare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ property_values[key] = to_assign
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ self.assertEqual(len(expected), property_values.getCount())
+ for i in range(property_values.getCount()):
+ self.assertEqual(to_compare[i][0].Name, property_values[i][0].Name)
+
+ def deleteValuesTestFixture(self, count, key, expected):
+ # Given
+ property_values = self.generateTestPropertyValues(count)
+ if not (isinstance(expected, Exception)):
+ to_compare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ del property_values[key]
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ self.assertEqual(len(expected), property_values.getCount())
+ for i in range(property_values.getCount()):
+ self.assertEqual(to_compare[i][0].Name, property_values[i][0].Name)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # obj[2:3] = val1,val2 # Insert/replace by slice
+ # obj[2:2] = (val,) # Insert by slice
+ # obj[2:4] = (val,) # Replace/delete by slice
+ # obj[2:3] = () # Delete by slice (implicit)
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexContainer_AssignSlice(self):
+ base_max = 5
+ assign_max = 5
+ for i in range(base_max):
+ for j in [x for x in range(-base_max-2, base_max+3)] + [None]:
+ for k in [x for x in range(-base_max-2, base_max+3)] + [None]:
+ key = slice(j, k)
+ for l in range(assign_max):
+ assign = [y+100 for y in range(l)]
+ expected = list(range(i))
+ expected[key] = assign
+ self.assignValuesTestFixture(i, key, assign, expected)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # obj[2:3] = val1,val2 # Insert/replace by slice
+ # obj[2:2] = (val,) # Insert by slice
+ # For:
+ # Cases not requiring sequence type coercion
+ # Invalid values
+ def test_XIndexContainer_AssignSlice_Invalid(self):
+ self.assignValuesTestFixture(2, slice(0, 2), None, TypeError())
+ self.assignValuesTestFixture(2, slice(0, 2), 'foo', TypeError())
+ self.assignValuesTestFixture(2, slice(0, 2), 12.34, TypeError())
+ self.assignValuesTestFixture(2, slice(0, 2), {'a': 'b'}, TypeError())
+ self.assignValuesTestFixture(2, slice(0, 2), ('foo',), TypeError())
+ self.assignValuesTestFixture(2, slice(0, 2), ('foo', 'foo'), TypeError())
+
+ # Tests syntax:
+ # obj[2:2] = (val,) # Insert by slice
+ # For:
+ # Cases not requiring sequence type coercion
+ def test_XIndexContainer_AssignSlice_NoCoercion(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ form = getDataFormInstance(doc)
+ form.Name = 'foo'
+
+ # When
+ doc.DrawPage.Forms[0:0] = (form,)
+
+ # Then
+ self.assertEqual('foo', doc.DrawPage.Forms[0].Name)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0:3:2] = val1,val2 # Replace by extended slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexContainer_AssignExtendedSlice(self):
+ base_max = 5
+ assign_max = 5
+ for i in range(base_max):
+ for j in [x for x in range(-base_max-2, base_max+3)] + [None]:
+ for k in [x for x in range(-base_max-2, base_max+3)] + [None]:
+ for l in [-2, -1, 1, 2]:
+ key = slice(j, k, l)
+ for m in range(assign_max):
+ assign = [y+100 for y in range(m)]
+ expected = list(range(i))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+
+ self.assignValuesTestFixture(i, key, assign, expected)
+
+ # Tests syntax:
+ # del obj[0] # Delete by index
+ def test_XIndexContainer_DelIndex(self):
+ base_max = 5
+ for i in range(base_max):
+ for j in [x for x in range(-base_max-2, base_max+3)]:
+ expected = list(range(i))
+ try:
+ del expected[j]
+ except Exception as e:
+ expected = e
+ self.deleteValuesTestFixture(i, j, expected)
+
+ # Tests syntax:
+ # del obj[2:4] # Delete by slice
+ def test_XIndexContainer_DelSlice(self):
+ baseMax = 5
+ for i in range(baseMax):
+ for j in [x for x in range(-baseMax-2, baseMax+3)] + [None]:
+ for k in [x for x in range(-baseMax-2, baseMax+3)] + [None]:
+ key = slice(j, k)
+ expected = list(range(i))
+ try:
+ del expected[key]
+ except Exception as e:
+ expected = e
+ self.deleteValuesTestFixture(i, key, expected)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XIndexReplace.py b/pyuno/qa/pytests/testcollections_XIndexReplace.py
new file mode 100644
index 0000000000..b2b2d22457
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexReplace.py
@@ -0,0 +1,240 @@
+#!/usr/bin/env python
+#
+# 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 uno
+
+from testcollections_base import CollectionsTestBase
+
+
+# ContentIndex instance factory
+def getContentIndexInstance(doc):
+ return doc.createInstance("com.sun.star.text.ContentIndex")
+
+# Tests behaviour of objects implementing XIndexReplace using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexReplace(CollectionsTestBase):
+
+ def generateTestContentIndex(self, doc):
+ index = getContentIndexInstance(doc)
+ for i in range(10):
+ styles = ('n'+str(i),)
+ uno.invoke(index.LevelParagraphStyles, "replaceByIndex", (i, uno.Any("[]string", styles)))
+ return index
+
+ def generateTestTuple(self, values):
+ properties = []
+ for i in values:
+ properties.append(('n'+str(i),),)
+ return tuple(properties)
+
+ def assignValuesTestFixture(self, doc, key, values, expected):
+ # Given
+ index = self.generateTestContentIndex(doc)
+ to_assign = self.generateTestTuple(values)
+ if not (isinstance(expected, Exception)):
+ toCompare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ index.LevelParagraphStyles[key] = to_assign
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ for i in range(10):
+ self.assertEqual(toCompare[i][0],
+ index.LevelParagraphStyles[i][0])
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceIndex(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When
+ index.LevelParagraphStyles[0] = ('Caption',)
+
+ # Then
+ self.assertEqual(('Caption',), index.LevelParagraphStyles[0])
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (None)
+ def test_XIndexReplace_ReplaceIndex_Invalid_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = None
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (String)
+ def test_XIndexReplace_ReplaceIndex_Invalid_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = 'foo'
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (Float)
+ def test_XIndexReplace_ReplaceIndex_Invalid_Float(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = 12.34
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (List)
+ def test_XIndexReplace_ReplaceIndex_Invalid_List(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = [0, 1]
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (Dict)
+ def test_XIndexReplace_ReplaceIndex_Invalid_Dict(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = {'a': 'b'}
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (inconsistently typed tuple)
+ def test_XIndexReplace_ReplaceIndex_Invalid_InconsistentTuple(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = ('Caption', ())
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceSlice(self):
+ assign_max = 12
+ doc = self.createBlankTextDocument()
+ t = tuple(range(10))
+ for j in [x for x in range(-12, 13)] + [None]:
+ for k in [x for x in range(-12, 13)] + [None]:
+ key = slice(j, k)
+ for l in range(assign_max):
+ assign = [y+100 for y in range(l)]
+ expected = list(range(10))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+ if (len(expected) != 10):
+ expected = ValueError()
+ self.assignValuesTestFixture(doc, key, assign, expected)
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # For:
+ # Invalid values (inconsistently value types in tuple)
+ def test_XIndexReplace_ReplaceSlice_Invalid_InconsistentTuple(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = getContentIndexInstance(doc)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0:2] = (
+ ('Caption',),
+ 12.34
+ )
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[0:3:2] = val1,val2 # Replace by extended slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceExtendedSlice(self):
+ assign_max = 12
+ doc = self.createBlankTextDocument()
+ t = tuple(range(10))
+ for j in [x for x in range(-12, 13)] + [None]:
+ for k in [x for x in range(-12, 13)] + [None]:
+ for l in [-2, -1, 2]:
+ key = slice(j, k, l)
+ for m in range(assign_max):
+ assign = [y+100 for y in range(m)]
+ expected = list(range(10))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+ self.assignValuesTestFixture(doc, key, assign, expected)
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XNameAccess.py b/pyuno/qa/pytests/testcollections_XNameAccess.py
new file mode 100644
index 0000000000..5c5ad6890b
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameAccess.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# Tests behaviour of objects implementing XNameAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameAccess(CollectionsTestBase):
+
+ # Tests syntax:
+ # num = len(obj) # Number of keys
+ # For:
+ # 2 elements
+ def test_XNameAccess_Len(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When
+ length_categories = len(drw.Links)
+ length_slides = len(drw.Links['Slide'].Links)
+ length_master = len(drw.Links['Master Page'].Links)
+
+ # Then
+ self.assertEqual(4, length_categories)
+ self.assertEqual(1, length_slides)
+ self.assertEqual(1, length_master)
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # 1/2 elements
+ def test_XNameAccess_ReadKey(self):
+ # Given
+ drw = self.createBlankDrawing()
+ drw.DrawPages[0].Name = 'foo'
+
+ # When
+ link = drw.Links['Slide'].Links['foo']
+
+ # Then
+ self.assertEqual('foo', link.getName())
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Missing key
+ def test_XNameAccess_ReadKey_Missing(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ link = drw.Links['Slide'].Links['foo']
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (None)
+ def test_XNameAccess_ReadKey_Invalid_None(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[None]
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (float)
+ def test_XNameAccess_ReadKey_Invalid_Float(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[12.34]
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (tuple)
+ def test_XNameAccess_ReadKey_Invalid_Tuple(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[(1, 2)]
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (list)
+ def test_XNameAccess_ReadKey_Invalid_List(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[[1, 2]]
+
+ drw.close(True)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (dict)
+ def test_XNameAccess_ReadKey_Invalid_Dict(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[{'a': 'b'}]
+
+ drw.close(True)
+
+ # Tests syntax:
+ # if key in obj: ... # Test key presence
+ # For:
+ # 1/2 elements
+ def test_XNameAccess_In(self):
+ # Given
+ drw = self.createBlankDrawing()
+ drw.DrawPages[0].Name = 'foo'
+
+ # When
+ present = 'foo' in drw.Links['Slide'].Links
+
+ # Then
+ self.assertTrue(present)
+
+ drw.close(True)
+
+ # Tests syntax:
+ # for key in obj: ... # Implicit iterator (keys)
+ # For:
+ # 2 elements
+ def test_XNameAccess_ForIn(self):
+ # Given
+ drw = self.createBlankDrawing()
+ i = 0
+ for name in drw.Links['Slide'].Links.getElementNames():
+ drw.Links['Slide'].Links.getByName(name).Name = 'foo' + str(i)
+ i += 1
+
+ # When
+ read_links = []
+ for link in drw.Links['Slide'].Links:
+ read_links.append(link)
+
+ # Then
+ self.assertEqual(['foo0'], read_links)
+
+ drw.close(True)
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator (keys)
+ # For:
+ # 2 elements
+ def test_XNameAccess_Iter(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When
+ itr = iter(drw.Links['Slide'].Links)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+ drw.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XNameContainer.py b/pyuno/qa/pytests/testcollections_XNameContainer.py
new file mode 100644
index 0000000000..a02cf4d648
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameContainer.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# SheetCellRanges instance factory
+def getSheetCellRangesInstance(spr):
+ return spr.createInstance("com.sun.star.sheet.SheetCellRanges")
+
+
+# Tests behaviour of objects implementing XNameContainer using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameContainer(CollectionsTestBase):
+
+ # Tests syntax:
+ # obj[key] = val # Insert by key
+ # For:
+ # 0->1 element
+ def test_XNameContainer_InsertName(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = getSheetCellRangesInstance(spr)
+ new_range = spr.Sheets[0][2:3,1:2]
+
+ # When
+ ranges['foo'] = new_range
+
+ # Then
+ self.assertEqual(1, len(ranges.ElementNames))
+
+ spr.close(True)
+
+ # Tests syntax:
+ # obj[key] = val # Insert by key
+ # For:
+ # Invalid key
+ def test_XNameContainer_InsertName_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = getSheetCellRangesInstance(spr)
+ new_range = spr.Sheets[0][2:3,1:2]
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ ranges[12.34] = new_range
+
+ spr.close(True)
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ def test_XNameContainer_ReplaceName(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = getSheetCellRangesInstance(spr)
+ new_range1 = spr.Sheets[0][2:3,1:2]
+ new_range2 = spr.Sheets[0][6:7,6:7]
+
+ # When
+ ranges['foo'] = new_range1
+ ranges['foo'] = new_range2
+
+ # Then
+ self.assertEqual(1, len(ranges.ElementNames))
+ read_range = ranges['foo']
+ self.assertEqual(6, read_range.CellAddress.Column)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # 1/2 elements
+ def test_XNameContainer_DelKey(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ spr.Sheets.insertNewByName('foo', 1)
+
+ # When
+ del spr.Sheets['foo']
+
+ # Then
+ self.assertEqual(1, len(spr.Sheets))
+ self.assertFalse('foo' in spr.Sheets)
+
+ spr.close(True)
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # Missing key
+ def test_XNameContainer_DelKey_Missing(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ del spr.Sheets['foo']
+
+ spr.close(True)
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # Invalid key (float)
+ def test_XNameContainer_DelKey_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ del spr.Sheets[12.34]
+
+ spr.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_XNameReplace.py b/pyuno/qa/pytests/testcollections_XNameReplace.py
new file mode 100644
index 0000000000..a0be04404f
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameReplace.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+def getScriptName():
+ return 'macro://Standard.Module1.MySave()'
+
+# Tests behaviour of objects implementing XNameReplace using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameReplace(CollectionsTestBase):
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # 1 element
+ def test_XNameReplace_ReplaceName(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ event_properties = (PropertyValue(Name='Script', Value=getScriptName()),)
+
+ # When
+ doc.Events['OnSave'] = event_properties
+
+ # Then
+ on_save = [p.Value for p in doc.Events['OnSave'] if p.Name == 'Script'][0]
+ self.assertEqual(getScriptName(), on_save)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # Invalid key
+ def test_XNameReplace_ReplaceName_Invalid(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ event_properties = (PropertyValue(Name='Script', Value=getScriptName()),)
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ doc.Events['qqqqq'] = event_properties
+
+ doc.close(True)
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # Invalid key type
+ def test_XNameReplace_ReplaceName_Invalid(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ event_properties = (PropertyValue(Name='Script', Value=getScriptName()),)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ doc.Events[12.34] = event_properties
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_base.py b/pyuno/qa/pytests/testcollections_base.py
new file mode 100644
index 0000000000..4dd3f26ea0
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_base.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# 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
+
+from org.libreoffice.unotest import pyuno
+from com.sun.star.beans import PropertyValue
+
+testEnvironmentInitialized = False
+
+class CollectionsTestBase(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.context = pyuno.getComponentContext()
+ global testEnvironmentInitialized
+ if not testEnvironmentInitialized:
+ pyuno.private_initTestEnvironment()
+ testEnvironmentInitialized = True
+
+ def setUp(self):
+ self._components = []
+
+ def tearDown(self):
+ for component in self._components:
+ try:
+ component.close(True)
+ except Exception:
+ pass
+
+ def createHiddenWindow(self, url):
+ service_manager = self.context.ServiceManager
+ desktop = service_manager.createInstanceWithContext('com.sun.star.frame.Desktop', self.context)
+ load_props = (
+ PropertyValue(Name='Hidden', Value=True),
+ PropertyValue(Name='ReadOnly', Value=False)
+ )
+ component = desktop.loadComponentFromURL(url, '_blank', 0, load_props)
+ return component
+
+ def createBlankTextDocument(self):
+ component = self.createHiddenWindow('private:factory/swriter')
+ self._components.append(component)
+ return component
+
+ def createBlankSpreadsheet(self):
+ component = self.createHiddenWindow('private:factory/scalc')
+ self._components.append(component)
+ return component
+
+ def createBlankDrawing(self):
+ component = self.createHiddenWindow('private:factory/sdraw')
+ self._components.append(component)
+ return component
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_misc.py b/pyuno/qa/pytests/testcollections_misc.py
new file mode 100644
index 0000000000..7ca40de1f7
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_misc.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# Miscellaneous tests of the behaviour of UNO objects using the new-style
+# collection accessors
+
+class TestMisc(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator
+ # For:
+ # Invalid type
+ def test_misc_IterateInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ for val in doc.UIConfigurationManager:
+ pass
+
+ doc.close(True)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Invalid type
+ def test_misc_InInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ foo = "bar" in doc.UIConfigurationManager
+
+ doc.close(True)
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # Invalid type
+ def test_misc_LenInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ len(doc.UIConfigurationManager)
+
+ doc.close(True)
+
+ # Tests syntax:
+ # val = obj[0] # Access by index
+ # For:
+ # Invalid type
+ def test_misc_SubscriptInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ doc.UIConfigurationManager[0]
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testcollections_misc2.py b/pyuno/qa/pytests/testcollections_misc2.py
new file mode 100644
index 0000000000..86bb544eac
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_misc2.py
@@ -0,0 +1,61 @@
+
+# execute run procedure as Python macro for testing
+
+import uno
+import unittest
+
+from com.sun.star.awt.FontSlant import ITALIC
+from com.sun.star.awt.FontSlant import NONE
+from com.sun.star.uno.TypeClass import STRING
+from com.sun.star.uno.TypeClass import LONG
+from com.sun.star.awt import Point
+
+class Test124953(unittest.TestCase):
+
+ def test_Enum(self):
+ italic = uno.Enum("com.sun.star.awt.FontSlant", "ITALIC")
+ none_ = uno.Enum("com.sun.star.awt.FontSlant", "NONE")
+ self.assertEqual(ITALIC, ITALIC)
+ self.assertEqual(ITALIC, italic)
+ self.assertFalse((ITALIC != italic))
+ self.assertNotEqual(ITALIC, NONE)
+ self.assertEqual(NONE, none_)
+
+ def test_Type(self):
+
+ STRING_TYPE = uno.getTypeByName("string")
+ LONG_TYPE = uno.getTypeByName("long")
+ string_type = uno.Type("string", STRING)
+ long_type = uno.Type("long", LONG)
+ self.assertEqual(STRING_TYPE, STRING_TYPE)
+ self.assertEqual(STRING_TYPE, string_type)
+ self.assertFalse((STRING_TYPE != string_type))
+ self.assertNotEqual(STRING_TYPE, LONG)
+ self.assertEqual(LONG_TYPE, long_type)
+
+ def test_Char(self):
+ char_a = uno.Char("a")
+ char_a2 = uno.Char("a")
+ char_b = uno.Char("b")
+ self.assertEqual(char_a, char_a)
+ self.assertEqual(char_a, char_a2)
+ self.assertFalse((char_a != char_a2))
+ self.assertNotEqual(char_a, char_b)
+
+ def test_ByteSequence(self):
+ b1 = uno.ByteSequence(bytes("abcdefg", encoding="utf8"))
+ b2 = uno.ByteSequence(bytes("abcdefg", encoding="utf8"))
+ b3 = uno.ByteSequence(bytes("1234567", encoding="utf8"))
+ self.assertEqual(b1, b1)
+ self.assertEqual(b1, b2)
+ self.assertFalse(b1 != b2)
+ self.assertNotEqual(b1, b3)
+
+ def test_Struct(self):
+ point1 = Point(100, 200)
+ point2 = Point(100, 200)
+ point3 = Point(0, 10)
+ self.assertEqual(point1, point1)
+ self.assertEqual(point1, point2)
+ self.assertFalse((point1 != point2))
+ self.assertNotEqual(point1, point3)
diff --git a/pyuno/qa/pytests/testcollections_mixednameindex.py b/pyuno/qa/pytests/testcollections_mixednameindex.py
new file mode 100644
index 0000000000..c363c0ca70
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_mixednameindex.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# 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
+
+from testcollections_base import CollectionsTestBase
+
+
+# Tests behaviour of objects implementing both XIndexAccess and XNameAccess
+# using the new-style collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestMixedNameIndex(CollectionsTestBase):
+
+ # Tests:
+ # Ability to access a dual XName*/XIndex* object by both name and index
+ def testWriterTextTableNameAndIndex(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ text_table = doc.createInstance("com.sun.star.text.TextTable")
+ text_table.initialize(2, 2)
+ text_table.Name = 'foo'
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, text_table, False)
+
+ # When
+ table_by_name = doc.TextTables['foo']
+ table_by_index = doc.TextTables[0]
+
+ # Then
+ self.assertEqual('foo', table_by_name.Name)
+ self.assertEqual('foo', table_by_index.Name)
+ self.assertEqual(table_by_name, table_by_index)
+
+ doc.close(True)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/pyuno/qa/pytests/testdocuments/fdo74824.ods b/pyuno/qa/pytests/testdocuments/fdo74824.ods
new file mode 100644
index 0000000000..122bae22d9
--- /dev/null
+++ b/pyuno/qa/pytests/testdocuments/fdo74824.ods
Binary files differ
diff --git a/pyuno/qa/pytests/testssl.py b/pyuno/qa/pytests/testssl.py
new file mode 100644
index 0000000000..6ec9875fb8
--- /dev/null
+++ b/pyuno/qa/pytests/testssl.py
@@ -0,0 +1,10 @@
+import unittest
+
+
+# I want to ensure that import ssl works on all platforms
+class SSLTest(unittest.TestCase):
+ def test_ssl_import(self):
+ import ssl
+
+if __name__ == '__main__':
+ unittest.main()
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:
diff --git a/pyuno/zipcore/mac.sh b/pyuno/zipcore/mac.sh
new file mode 100644
index 0000000000..830bcdaa10
--- /dev/null
+++ b/pyuno/zipcore/mac.sh
@@ -0,0 +1,14 @@
+# Set URE_BOOTSTRAP so that "uno.getComponentContext()" bootstraps a complete
+# OOo UNO environment:
+: ${URE_BOOTSTRAP=vnd.sun.star.pathname:$sd_prog/../Resources/fundamentalrc}
+export URE_BOOTSTRAP
+
+PYTHONHOME=$sd_prog/../Frameworks/LibreOfficePython.framework
+export PYTHONHOME
+
+pybasislibdir=$PYTHONHOME/Versions/%%PYVERSION%%/lib/python%%PYVERSION%%
+PYTHONPATH=$sd_prog/../Resources:$sd_prog/../Frameworks:$pybasislibdir:$pybasislibdir/lib-dynload:$pybasislibdir/lib-tk:$pybasislibdir/site-packages${PYTHONPATH+:$PYTHONPATH}
+export PYTHONPATH
+
+# execute binary
+exec "$PYTHONHOME/Versions/%%PYVERSION%%/Resources/Python.app/Contents/MacOS/LibreOfficePython" "$@"
diff --git a/pyuno/zipcore/nonmac.sh b/pyuno/zipcore/nonmac.sh
new file mode 100644
index 0000000000..5e7cca14d0
--- /dev/null
+++ b/pyuno/zipcore/nonmac.sh
@@ -0,0 +1,12 @@
+# Set URE_BOOTSTRAP so that "uno.getComponentContext()" bootstraps a complete
+# OOo UNO environment:
+: ${URE_BOOTSTRAP=vnd.sun.star.pathname:$sd_prog/fundamentalrc}
+export URE_BOOTSTRAP
+
+PYTHONPATH=$sd_prog:$sd_prog/python-core-%%PYVERSION%%/lib:$sd_prog/python-core-%%PYVERSION%%/lib/lib-dynload:$sd_prog/python-core-%%PYVERSION%%/lib/lib-tk:$sd_prog/python-core-%%PYVERSION%%/lib/site-packages${PYTHONPATH+:$PYTHONPATH}
+export PYTHONPATH
+PYTHONHOME=$sd_prog/python-core-%%PYVERSION%%
+export PYTHONHOME
+
+# execute binary
+exec "$sd_prog/python.bin" "$@"
diff --git a/pyuno/zipcore/python.cxx b/pyuno/zipcore/python.cxx
new file mode 100644
index 0000000000..7580e32789
--- /dev/null
+++ b/pyuno/zipcore/python.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 <config_python.h>
+
+#include <cstddef>
+#include <stdlib.h>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <tools/pathutils.hxx>
+
+#define MY_LENGTH(s) (sizeof (s) / sizeof *(s) - 1)
+#define MY_STRING(s) (s), MY_LENGTH(s)
+
+static wchar_t * encode(wchar_t * buffer, wchar_t const * text) {
+ *buffer++ = L'"';
+ std::size_t n = 0;
+ for (;;) {
+ wchar_t c = *text++;
+ if (c == L'\0') {
+ break;
+ } else if (c == L'"') {
+ // Double any preceding backslashes as required by Windows:
+ for (std::size_t i = 0; i < n; ++i) {
+ *buffer++ = L'\\';
+ }
+ *buffer++ = L'\\';
+ *buffer++ = L'"';
+ n = 0;
+ } else if (c == L'\\') {
+ *buffer++ = L'\\';
+ ++n;
+ } else {
+ *buffer++ = c;
+ n = 0;
+ }
+ }
+ // The command line will continue with a double quote, so double any
+ // preceding backslashes as required by Windows:
+ for (std::size_t i = 0; i < n; ++i) {
+ *buffer++ = L'\\';
+ }
+ *buffer++ = L'"';
+ return buffer;
+}
+
+int wmain(int argc, wchar_t ** argv, wchar_t **) {
+ wchar_t path[MAX_PATH];
+ DWORD n = GetModuleFileNameW(nullptr, path, MAX_PATH);
+ if (n == 0 || n >= MAX_PATH) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t * pathEnd = tools::filename(path);
+ *pathEnd = L'\0';
+ n = GetEnvironmentVariableW(L"UNO_PATH", nullptr, 0);
+ if (n == 0) {
+ if (GetLastError() != ERROR_ENVVAR_NOT_FOUND ||
+ !SetEnvironmentVariableW(L"UNO_PATH", path))
+ {
+ exit(EXIT_FAILURE);
+ }
+ }
+ wchar_t bootstrap[MY_LENGTH(L"vnd.sun.star.pathname:") + MAX_PATH] =
+ L"vnd.sun.star.pathname:"; //TODO: overflow
+ wchar_t * bootstrapEnd = tools::buildPath(
+ bootstrap + MY_LENGTH(L"vnd.sun.star.pathname:"), path, pathEnd,
+ MY_STRING(L"fundamental.ini"));
+ if (bootstrapEnd == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t pythonpath2[MAX_PATH];
+ wchar_t * pythonpath2End = tools::buildPath(
+ pythonpath2, path, pathEnd,
+ MY_STRING(L"\\python-core-" PYTHON_VERSION_STRING L"\\lib"));
+ if (pythonpath2End == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t pythonpath3[MAX_PATH];
+ wchar_t * pythonpath3End = tools::buildPath(
+ pythonpath3, path, pathEnd,
+ MY_STRING(L"\\python-core-" PYTHON_VERSION_STRING L"\\lib\\site-packages"));
+ if (pythonpath3End == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t pythonhome[MAX_PATH];
+ wchar_t * pythonhomeEnd = tools::buildPath(
+ pythonhome, path, pathEnd, MY_STRING(L"\\python-core-" PYTHON_VERSION_STRING));
+ if (pythonhomeEnd == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ wchar_t pythonexe[MAX_PATH];
+ wchar_t * pythonexeEnd = tools::buildPath(
+ pythonexe, path, pathEnd,
+ MY_STRING(L"\\python-core-" PYTHON_VERSION_STRING L"\\bin\\python.exe"));
+ if (pythonexeEnd == nullptr) {
+ exit(EXIT_FAILURE);
+ }
+ std::size_t clSize = MY_LENGTH(L"\"") + 4 * (pythonexeEnd - pythonexe) +
+ MY_LENGTH(L"\"\0"); //TODO: overflow
+ // 4 * len: each char preceded by backslash, each trailing backslash
+ // doubled
+ for (int i = 1; i < argc; ++i) {
+ clSize += MY_LENGTH(L" \"") + 4 * wcslen(argv[i]) + MY_LENGTH(L"\"");
+ //TODO: overflow
+ }
+ wchar_t * cl = new wchar_t[clSize];
+ wchar_t * cp = encode(cl, pythonhome);
+ for (int i = 1; i < argc; ++i) {
+ *cp++ = L' ';
+ cp = encode(cp, argv[i]);
+ }
+ *cp = L'\0';
+ n = GetEnvironmentVariableW(L"PATH", nullptr, 0);
+ wchar_t * orig;
+ if (n == 0) {
+ if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
+ exit(EXIT_FAILURE);
+ }
+ orig = const_cast<wchar_t *>(L"");
+ } else {
+ orig = new wchar_t[n];
+ if (GetEnvironmentVariableW(L"PATH", orig, n) != n - 1)
+ {
+ exit(EXIT_FAILURE);
+ }
+ }
+ std::size_t len = (pathEnd - path) + (n == 0 ? 0 : MY_LENGTH(L";") +
+ (n - 1)) + 1;
+ //TODO: overflow
+ wchar_t * value = new wchar_t[len];
+ _snwprintf(
+ value, len, L"%s%s%s", path, n == 0 ? L"" : L";", orig);
+ if (!SetEnvironmentVariableW(L"PATH", value)) {
+ exit(EXIT_FAILURE);
+ }
+ if (n != 0) {
+ delete [] orig;
+ }
+ delete [] value;
+ n = GetEnvironmentVariableW(L"PYTHONPATH", nullptr, 0);
+ if (n == 0) {
+ if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) {
+ exit(EXIT_FAILURE);
+ }
+ orig = const_cast<wchar_t *>(L"");
+ } else {
+ orig = new wchar_t[n];
+ if (GetEnvironmentVariableW(L"PYTHONPATH", orig, n) != n - 1)
+ {
+ exit(EXIT_FAILURE);
+ }
+ }
+ len = (pathEnd - path) + MY_LENGTH(L";") + (pythonpath2End - pythonpath2) +
+ MY_LENGTH(L";") + (pythonpath3End - pythonpath3) +
+ (n == 0 ? 0 : MY_LENGTH(L";") + (n - 1)) + 1; //TODO: overflow
+ value = new wchar_t[len];
+ _snwprintf(
+ value, len, L"%s;%s;%s%s%s", path, pythonpath2, pythonpath3,
+ n == 0 ? L"" : L";", orig);
+ if (!SetEnvironmentVariableW(L"PYTHONPATH", value)) {
+ exit(EXIT_FAILURE);
+ }
+ if (n != 0) {
+ delete [] orig;
+ }
+ delete [] value;
+ if (!SetEnvironmentVariableW(L"PYTHONHOME", pythonhome)) {
+ exit(EXIT_FAILURE);
+ }
+ n = GetEnvironmentVariableW(L"URE_BOOTSTRAP", nullptr, 0);
+ if (n == 0) {
+ if (GetLastError() != ERROR_ENVVAR_NOT_FOUND ||
+ !SetEnvironmentVariableW(L"URE_BOOTSTRAP", bootstrap))
+ {
+ exit(EXIT_FAILURE);
+ }
+ }
+ STARTUPINFOW startinfo;
+ ZeroMemory(&startinfo, sizeof (STARTUPINFOW));
+ startinfo.cb = sizeof (STARTUPINFOW);
+ PROCESS_INFORMATION procinfo;
+ if (!CreateProcessW(
+ pythonexe, cl, nullptr, nullptr, FALSE, CREATE_UNICODE_ENVIRONMENT, nullptr,
+ nullptr, &startinfo, &procinfo)) {
+ exit(EXIT_FAILURE);
+ }
+ WaitForSingleObject(procinfo.hProcess,INFINITE);
+ DWORD exitStatus;
+ GetExitCodeProcess(procinfo.hProcess,&exitStatus);
+ exit(exitStatus);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/zipcore/python.sh b/pyuno/zipcore/python.sh
new file mode 100755
index 0000000000..bfe6785bd6
--- /dev/null
+++ b/pyuno/zipcore/python.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# 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 .
+#
+
+# resolve installation directory
+sd_cwd="`pwd`"
+if [ -h "$0" ] ; then
+ sd_basename=`basename "$0"`
+ sd_script=`ls -l "$0" | sed "s/.*${sd_basename} -> //g"`
+ cd "`dirname "$0"`"
+ cd "`dirname "$sd_script"`"
+else
+ cd "`dirname "$0"`"
+fi
+sd_prog=`pwd`
+cd "$sd_cwd"
+
+# Set PATH so that crash_report is found:
+PATH=$sd_prog${PATH+:$PATH}
+export PATH
+
+# Set UNO_PATH so that "officehelper.bootstrap()" can find soffice executable:
+: ${UNO_PATH=$sd_prog}
+export UNO_PATH
+