# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# 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 subprocess
import time
import traceback
import uuid
import os
try:
import pyuno
import uno
except ImportError:
print("pyuno not found: try to set PYTHONPATH and URE_BOOTSTRAP variables")
print("PYTHONPATH=/installation/opt/program")
print("URE_BOOTSTRAP=file:///installation/opt/program/fundamentalrc")
raise
class OfficeConnection:
def __init__(self, args):
self.args = args
self.soffice = None
self.xContext = None
def setUp(self):
""" Create a new connection to a LibreOffice process
If the connection method is path the instance will be created as a
new subprocess. If the connection method is connect the instance tries
to connect to an existing instance with the specified socket string """
(method, sep, rest) = self.args["--soffice"].partition(":")
if sep != ":":
raise Exception("soffice parameter does not specify method")
if method == "path":
socket = "pipe,name=pytest" + str(uuid.uuid1())
try:
userdir = self.args["--userdir"]
except KeyError:
raise Exception("'path' method requires --userdir")
if not(userdir.startswith("file://")):
raise Exception("--userdir must be file URL")
self.soffice = self.bootstrap(rest, userdir, socket)
elif method == "connect":
socket = rest
else:
raise Exception("unsupported connection method: " + method)
# connect to the soffice instance
success = False
try:
self.xContext = self.connect(socket)
success = True
finally:
if not success and self.soffice:
self.soffice.terminate()
self.soffice.wait()
self.soffice = None
def bootstrap(self, soffice, userdir, socket):
""" Creates a new LibreOffice process
@param soffice Path to the soffice installation
@param userdir Directory of the user profile, only one process per user
profile is possible
@param socket The socket string used for the PyUNO connection """
argv = [soffice, "--accept=" + socket + ";urp",
"-env:UserInstallation=" + userdir,
"--quickstart=no", "--nofirststartwizard",
"--norestore", "--nologo"]
if "--valgrind" in self.args:
argv.append("--valgrind")
if "--gdb" in self.args:
argv.insert(0, "gdb")
argv.insert(1, "-ex")
argv.insert(2, "run")
argv.insert(3, "--args")
argv[4] = argv[4].replace("soffice", "soffice.bin")
env = None
environ = dict(os.environ)
if 'LIBO_LANG' in environ:
env = environ
env['LC_ALL'] = environ['LIBO_LANG']
return subprocess.Popen(argv, env=env)
def connect(self, socket):
""" Tries to connect to the LibreOffice instance through the specified socket"""
xLocalContext = uno.getComponentContext()
xUnoResolver = xLocalContext.ServiceManager.createInstanceWithContext(
"com.sun.star.bridge.UnoUrlResolver", xLocalContext)
url = "uno:" + socket + ";urp;StarOffice.ComponentContext"
print("OfficeConnection: connecting to: " + url)
while True:
if self.soffice and self.soffice.poll() is not None:
raise Exception("soffice has stopped.")
try:
xContext = xUnoResolver.resolve(url)
return xContext
except pyuno.getClass("com.sun.star.connection.NoConnectException"):
print("NoConnectException: sleeping...")
time.sleep(1)
def tearDown(self):
"""Terminate a LibreOffice instance created with the path connection method.
Tries to terminate the soffice instance through the normal
XDesktop::terminate method and waits indefinitely for the subprocess
to terminate """
if self.soffice:
if self.xContext:
try:
print("tearDown: calling terminate()...")
xMgr = self.xContext.ServiceManager
xDesktop = xMgr.createInstanceWithContext(
"com.sun.star.frame.Desktop", self.xContext)
xDesktop.terminate()
print("...done")
except pyuno.getClass("com.sun.star.beans.UnknownPropertyException"):
print("caught while TearDown:\n", traceback.format_exc())
pass # ignore, also means disposed
except pyuno.getClass("com.sun.star.lang.DisposedException"):
print("caught while TearDown:\n", traceback.format_exc())
pass # ignore
else:
self.soffice.terminate()
ret = self.soffice.wait()
self.xContext = None
self.soffice = None
if ret != 0:
raise Exception("Exit status indicates failure: " + str(ret))
@classmethod
def getHelpText(cls):
message = """
--soffice=method:location
specify soffice instance to connect to
supported methods: 'path', 'connect'
--userdir=URL specify user installation directory for 'path' method
--valgrind pass --valgrind to soffice for 'path' method
'location' is a pathname, not a URL. 'userdir' is a URL.
"""
return message
class PersistentConnection:
def __init__(self, args):
self.args = args
self.connection = None
def getContext(self):
""" Returns the XContext corresponding to the LibreOffice instance
This is the starting point for any PyUNO access to the LibreOffice
instance."""
return self.connection.xContext
def setUp(self):
# don't create two connections
if self.connection:
return
conn = OfficeConnection(self.args)
conn.setUp()
self.connection = conn
def tearDown(self):
if self.connection:
try:
self.connection.tearDown()
finally:
self.connection = None
def kill(self):
""" Kills the LibreOffice instance if it was created through the connection
Only works with the connection method path"""
if self.connection and self.connection.soffice:
self.connection.soffice.kill()
# vim: set shiftwidth=4 softtabstop=4 expandtab:
istro/lhm/libreoffice-7-6+backports