| |
| 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__) |
|
|