Spaces:
Running
Running
| # -*- coding: utf-8 -*- | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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__) | |