|
|
|
|
|
import logging |
|
|
import sys |
|
|
import os |
|
|
|
|
|
logfmt = logging.Formatter((' backend %5d ' % os.getpid()) |
|
|
+'%(message)s') |
|
|
logger = logging.getLogger('backend') |
|
|
logger.setLevel(logging.INFO) |
|
|
|
|
|
|
|
|
logger.info('backend: %s', os.getpid()) |
|
|
logger.info('sys.executable = %s', sys.executable) |
|
|
|
|
|
import unohelper |
|
|
g_ImplementationHelper = unohelper.ImplementationHelper() |
|
|
|
|
|
def implementation(component_name, *services): |
|
|
def decorator(cls): |
|
|
g_ImplementationHelper.addImplementation(cls, component_name, services) |
|
|
return cls |
|
|
return decorator |
|
|
|
|
|
|
|
|
from com.sun.star.task import XJob |
|
|
|
|
|
@implementation('backend.TestRunnerJob') |
|
|
class TestRunnerJob(unohelper.Base, XJob): |
|
|
|
|
|
def __init__(self, context): |
|
|
self.context = context |
|
|
|
|
|
def execute(self, arguments): |
|
|
import sys |
|
|
args = dict((nv.Name, nv.Value) for nv in arguments) |
|
|
|
|
|
cwd = os.getcwd() |
|
|
working_dir = args['working_dir'] |
|
|
os.chdir(working_dir) |
|
|
try: |
|
|
logstream = args['logstream'] |
|
|
logstream = FileFromStream(logstream) |
|
|
loghandler = logging.StreamHandler(logstream) |
|
|
loghandler.setFormatter(logfmt) |
|
|
logger.addHandler(loghandler) |
|
|
try: |
|
|
logger.info('current dir: %s', cwd) |
|
|
logger.info('working dir: %s', working_dir) |
|
|
logger.info('sys.path:') |
|
|
for x in sys.path: |
|
|
logger.info('- %s', x) |
|
|
return self.run(args) |
|
|
finally: |
|
|
logger.removeHandler(loghandler) |
|
|
finally: |
|
|
os.chdir(cwd) |
|
|
|
|
|
def run(self, args): |
|
|
import cPickle |
|
|
outstream = args.get('outputstream') |
|
|
outstream = FileFromStream(outstream) |
|
|
|
|
|
extra_path = args.get('extra_path') |
|
|
if extra_path: |
|
|
logger.info('extra_path: %s', ' '.join(extra_path)) |
|
|
sys.path.extend(extra_path) |
|
|
|
|
|
logconf_path = args.get('logconf_path') |
|
|
if logconf_path: |
|
|
import logging.config |
|
|
logging.config.fileConfig(logconf_path) |
|
|
|
|
|
from hwp5.plat import _uno |
|
|
_uno.enable() |
|
|
|
|
|
pickled_testsuite = args.get('pickled_testsuite') |
|
|
if not pickled_testsuite: |
|
|
logger.error('pickled_testsuite is required') |
|
|
return cPickle.dumps(dict(successful=False, tests=0, failures=0, |
|
|
errors=0)) |
|
|
|
|
|
pickled_testsuite = str(pickled_testsuite) |
|
|
testsuite = cPickle.loads(pickled_testsuite) |
|
|
logger.info('Test Suite Unpickled') |
|
|
|
|
|
from unittest import TextTestRunner |
|
|
testrunner = TextTestRunner(stream=outstream) |
|
|
result = testrunner.run(testsuite) |
|
|
result = dict(successful=result.wasSuccessful(), |
|
|
tests=result.testsRun, |
|
|
failures=list(str(x) for x in result.failures), |
|
|
errors=list(str(x) for x in result.errors)) |
|
|
return cPickle.dumps(result) |
|
|
|
|
|
|
|
|
import contextlib |
|
|
|
|
|
|
|
|
@contextlib.contextmanager |
|
|
def sandbox(working_dir, **kwargs): |
|
|
import os |
|
|
import sys |
|
|
|
|
|
backup = dict() |
|
|
class NOTHING: |
|
|
pass |
|
|
if not hasattr(sys, 'argv'): |
|
|
sys.argv = NOTHING |
|
|
|
|
|
NAMES = ['path', 'argv', 'stdin', 'stdout', 'stderr'] |
|
|
for x in NAMES: |
|
|
assert x in kwargs |
|
|
|
|
|
backup['cwd'] = os.getcwd() |
|
|
os.chdir(working_dir) |
|
|
for x in NAMES: |
|
|
backup[x] = getattr(sys, x) |
|
|
setattr(sys, x, kwargs[x]) |
|
|
|
|
|
try: |
|
|
yield |
|
|
finally: |
|
|
for x in NAMES: |
|
|
setattr(sys, x, backup[x]) |
|
|
os.chdir(backup['cwd']) |
|
|
|
|
|
if sys.argv is NOTHING: |
|
|
del sys.argv |
|
|
|
|
|
|
|
|
@implementation('backend.RemoteRunJob') |
|
|
class RemoteRunJob(unohelper.Base, XJob): |
|
|
|
|
|
def __init__(self, context): |
|
|
self.context = context |
|
|
|
|
|
def execute(self, arguments): |
|
|
args = dict((nv.Name, nv.Value) for nv in arguments) |
|
|
|
|
|
logpath = args.get('logfile') |
|
|
if logpath is not None: |
|
|
logfile = file(logpath, 'a') |
|
|
loghandler = logging.StreamHandler(logfile) |
|
|
logger.addHandler(loghandler) |
|
|
|
|
|
import datetime |
|
|
logger.info('-'*10 + (' start at %s' % datetime.datetime.now()) + '-'*10) |
|
|
try: |
|
|
return self.run(args) |
|
|
finally: |
|
|
logger.info('-'*10 + (' stop at %s' % datetime.datetime.now()) + '-'*10) |
|
|
if logpath is not None: |
|
|
logger.removeHandler(loghandler) |
|
|
|
|
|
def run(self, args): |
|
|
import cPickle |
|
|
|
|
|
working_dir = args['working_dir'] |
|
|
path = cPickle.loads(str(args['path'])) |
|
|
argv = cPickle.loads(str(args['argv'])) |
|
|
stdin = FileFromStream(args['stdin']) |
|
|
stdout = FileFromStream(args['stdout']) |
|
|
stderr = FileFromStream(args['stderr']) |
|
|
|
|
|
script = argv[0] |
|
|
with sandbox(working_dir, path=path, argv=argv, stdin=stdin, |
|
|
stdout=stdout, stderr=stderr): |
|
|
g = dict(__name__='__main__') |
|
|
try: |
|
|
execfile(script, g) |
|
|
except SystemExit, e: |
|
|
return e.code |
|
|
except Exception, e: |
|
|
logger.exception(e) |
|
|
raise |
|
|
except: |
|
|
import traceback |
|
|
logger.error('%s' % traceback.format_exc()) |
|
|
raise |
|
|
|
|
|
|
|
|
@implementation('backend.ConsoleJob') |
|
|
class ConsoleJob(unohelper.Base, XJob): |
|
|
|
|
|
def __init__(self, context): |
|
|
self.context = context |
|
|
|
|
|
def execute(self, arguments): |
|
|
args = dict((nv.Name, nv.Value) for nv in arguments) |
|
|
|
|
|
cwd = os.getcwd() |
|
|
try: |
|
|
inp = args['inp'] |
|
|
outstream = args['outstream'] |
|
|
|
|
|
outfile = FileFromStream(outstream) |
|
|
|
|
|
import sys |
|
|
orig = sys.stdout, sys.stderr |
|
|
sys.stdout = sys.stderr = outfile |
|
|
try: |
|
|
console = Console(inp, outfile) |
|
|
try: |
|
|
console.interact('LibreOffice Python Console (pid: %s)' % |
|
|
os.getpid()) |
|
|
return 0 |
|
|
except SystemExit, e: |
|
|
return e.code |
|
|
finally: |
|
|
sys.stdout, sys.stderr = orig |
|
|
finally: |
|
|
os.chdir(cwd) |
|
|
|
|
|
|
|
|
from code import InteractiveConsole |
|
|
class Console(InteractiveConsole): |
|
|
|
|
|
def __init__(self, inp, outfile): |
|
|
InteractiveConsole.__init__(self) |
|
|
self.inp = inp |
|
|
self.outfile = outfile |
|
|
|
|
|
def write(self, data): |
|
|
self.outfile.write(data) |
|
|
self.outfile.flush() |
|
|
|
|
|
def raw_input(self, prompt=''): |
|
|
import uno |
|
|
arg = uno.createUnoStruct('com.sun.star.beans.NamedValue') |
|
|
arg.Name = 'prompt' |
|
|
arg.Value = prompt |
|
|
args = arg, |
|
|
result = self.inp.execute(args) |
|
|
if result is None: |
|
|
raise EOFError() |
|
|
return result |
|
|
|
|
|
|
|
|
class FileFromStream(object): |
|
|
''' A file-like object based on XInputStream/XOuputStream/XSeekable |
|
|
|
|
|
:param stream: a stream object which implements |
|
|
com.sun.star.io.XInputStream, com.sun.star.io.XOutputStream or |
|
|
com.sun.star.io.XSeekable |
|
|
''' |
|
|
def __init__(self, stream, encoding='utf-8'): |
|
|
import uno |
|
|
self.stream = stream |
|
|
self.encoding = encoding |
|
|
|
|
|
if hasattr(stream, 'readBytes'): |
|
|
def read(size=None): |
|
|
if size is None: |
|
|
data = '' |
|
|
while True: |
|
|
bytes = uno.ByteSequence('') |
|
|
n_read, bytes = stream.readBytes(bytes, 4096) |
|
|
if n_read == 0: |
|
|
return data |
|
|
data += bytes.value |
|
|
bytes = uno.ByteSequence('') |
|
|
n_read, bytes = stream.readBytes(bytes, size) |
|
|
return bytes.value |
|
|
self.read = read |
|
|
|
|
|
if hasattr(stream, 'seek'): |
|
|
self.tell = stream.getPosition |
|
|
|
|
|
def seek(offset, whence=0): |
|
|
if whence == 0: |
|
|
pass |
|
|
elif whence == 1: |
|
|
offset += stream.getPosition() |
|
|
elif whence == 2: |
|
|
offset += stream.getLength() |
|
|
stream.seek(offset) |
|
|
self.seek = seek |
|
|
|
|
|
if hasattr(stream, 'writeBytes'): |
|
|
def write(s): |
|
|
if isinstance(s, unicode): |
|
|
s = s.encode(self.encoding) |
|
|
stream.writeBytes(uno.ByteSequence(s)) |
|
|
self.write = write |
|
|
|
|
|
def flush(): |
|
|
stream.flush() |
|
|
self.flush = flush |
|
|
|
|
|
def close(self): |
|
|
if hasattr(self.stream, 'closeInput'): |
|
|
self.stream.closeInput() |
|
|
elif hasattr(self.stream, 'closeOutput'): |
|
|
self.stream.closeOutput() |
|
|
|
|
|
|
|
|
''' |
|
|
import os.path |
|
|
from uno import systemPathToFileUrl |
|
|
from unokit.ucb import open_url |
|
|
from unokit.services import css |
|
|
|
|
|
path = os.path.abspath('samples/sample-5017.hwp') |
|
|
print path |
|
|
url = systemPathToFileUrl(path) |
|
|
print url |
|
|
inp = open_url(url) |
|
|
print inp |
|
|
|
|
|
# 여기서 segfault |
|
|
stg = css.embed.OLESimpleStorage(inp) |
|
|
print stg |
|
|
''' |
|
|
|
|
|
|
|
|
''' |
|
|
SegFault가 나는 stacktrace는 다음과 같다 |
|
|
|
|
|
StgDirEntry::StgDirEntry |
|
|
sot/source/sdstor/ |
|
|
StgEntry::Load |
|
|
sot/source/sdstor/ |
|
|
ToUpperUnicode |
|
|
sot/source/sdstor/stgelem.cxx |
|
|
CharClass |
|
|
unotools/source/i18n/charclass.cxx |
|
|
intl_createInstance |
|
|
unotools/source/i18n/instance.hxx |
|
|
|
|
|
여기서 ::comphelper::getProcessServiceFactory로 얻은 XMultiServiceFactory가 |
|
|
null 값인 것 같다. |
|
|
|
|
|
---- |
|
|
|
|
|
uno를 사용하는 프로그램들 (desktop app/unopkg, 각종 unittest 프로그램)은 |
|
|
처음 실행 시 다음과 같은 과정을 거치는 듯 하다. |
|
|
|
|
|
1. ::cppu::defaultBootstrap_InitialComponentContext()을 호출, local context 생성 |
|
|
- unorc 등을 검색, application.rdb, user.rdb 등에 접근 |
|
|
|
|
|
- pyuno.so 의 getComponentContext()에서 수행: PYUNOLIBDIR 즉 pyuno.so가 있는 |
|
|
디렉토리에서 pyuno.rc가 있으면 그것으로 초기화) |
|
|
- desktop app: appinit.cxx의 CreateApplicationServiceManager()에서 수행 |
|
|
- unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 수행. |
|
|
ucbhelper::ContentBroker도 함께 초기화한다. |
|
|
|
|
|
2. 이렇게 생성한 local context로부터 ServiceManager를 얻어 |
|
|
::comphelper::setProcessServiceFactory()를 사용하여 프로세스 전역 service |
|
|
factory로 등록 |
|
|
|
|
|
- uno.py와 pyuno.so는 이 작업을 하지 않는다. |
|
|
- desktop app: app.cxx의 ensureProcessServiceFactory()에서 수행 |
|
|
- unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 함께 수행. |
|
|
|
|
|
|
|
|
* desktop app: desktop/source/app/ |
|
|
* unopkg: desktop/source/pkgchk/unopkg/ |
|
|
|
|
|
''' |
|
|
|
|
|
logger.info('%s: end of file', __name__) |
|
|
|