Add tools
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- tools/README.rst +19 -0
- tools/constants/pyhwp_dev_constants.py +20 -0
- tools/constants/setup.py +4 -0
- tools/discover.lo/discover_lo.py +275 -0
- tools/discover.lo/setup.py +5 -0
- tools/discover.lxml/discover_lxml.py +120 -0
- tools/discover.lxml/setup.py +22 -0
- tools/discover.python/discover_python.py +222 -0
- tools/discover.python/setup.py +19 -0
- tools/download/pyhwp_download.py +71 -0
- tools/download/setup.py +6 -0
- tools/egg.path/egg_path.py +30 -0
- tools/egg.path/setup.py +9 -0
- tools/gpl/gpl/Pysec.py +432 -0
- tools/gpl/gpl/__init__.py +85 -0
- tools/gpl/gpl/parsers.py +202 -0
- tools/gpl/gpl/tests/__init__.py +0 -0
- tools/gpl/gpl/tests/test_gpl.py +171 -0
- tools/gpl/setup.py +9 -0
- tools/jingodf/jingodf/__init__.py +176 -0
- tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.0-os.rng +111 -0
- tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.1.rng +111 -0
- tools/jingodf/jingodf/schema/OpenDocument-schema-v1.0-os.rng +0 -0
- tools/jingodf/jingodf/schema/OpenDocument-schema-v1.1.rng +0 -0
- tools/jingodf/jingodf/schema/OpenDocument-strict-schema-v1.1.rng +61 -0
- tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-dsig-schema.rng +84 -0
- tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-manifest-schema.rng +224 -0
- tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-metadata.owl +86 -0
- tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-package-metadata.owl +81 -0
- tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-schema.rng +0 -0
- tools/jingodf/setup.py +13 -0
- tools/jxml/jxml.coverage/jxml_coverage.py +117 -0
- tools/jxml/jxml.coverage/setup.py +16 -0
- tools/jxml/jxml/__init__.py +0 -0
- tools/jxml/jxml/etree/__init__.py +1072 -0
- tools/jxml/lxml/lxml/__init__.py +1 -0
- tools/jxml/lxml/lxml/etree/__init__.py +1 -0
- tools/jxml/lxml/setup.py +9 -0
- tools/jxml/setup.py +9 -0
- tools/jxml/tests/Makefile +52 -0
- tools/jxml/tests/hello.xml +1 -0
- tools/jxml/tests/sample.xml +5 -0
- tools/jxml/tests/test_jaxp.py +69 -0
- tools/jxml/tests/test_lxml.py +664 -0
- tools/jxml/tests/text-output.xsl +7 -0
- tools/jxml/tests/xsl/import-test.xsl +10 -0
- tools/jxml/tests/xsl/imported.xsl +7 -0
- tools/oxt.tool/oxt_tool/__init__.py +405 -0
- tools/oxt.tool/oxt_tool/backend.py +361 -0
- tools/oxt.tool/oxt_tool/description.py +379 -0
tools/README.rst
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
``tools/`` - Development helper packages
|
| 2 |
+
----------------------------------------
|
| 3 |
+
|
| 4 |
+
``discover.python/``
|
| 5 |
+
``discover.lxml/``
|
| 6 |
+
``discover.jre/``
|
| 7 |
+
``discover.lo/``
|
| 8 |
+
``install.jython/``
|
| 9 |
+
|
| 10 |
+
Discover multiple python versions, lxml, JRE, Libreoffice to use in the
|
| 11 |
+
developement environment. Provides zc.buildout recipes.
|
| 12 |
+
|
| 13 |
+
``xsltest/``
|
| 14 |
+
|
| 15 |
+
an XSLT test runner.
|
| 16 |
+
|
| 17 |
+
``oxt.tool/``
|
| 18 |
+
|
| 19 |
+
Build and test .oxt packages with the LibreOffice.
|
tools/constants/pyhwp_dev_constants.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Recipe(object):
|
| 7 |
+
|
| 8 |
+
def __init__(self, buildout, name, options):
|
| 9 |
+
options['pathsep'] = os.pathsep
|
| 10 |
+
options['sep'] = os.sep
|
| 11 |
+
if sys.platform == 'win32':
|
| 12 |
+
options['script_py_suffix'] = '-script.py'
|
| 13 |
+
else:
|
| 14 |
+
options['script_py_suffix'] = ''
|
| 15 |
+
|
| 16 |
+
def install(self):
|
| 17 |
+
return []
|
| 18 |
+
|
| 19 |
+
def update(self):
|
| 20 |
+
pass
|
tools/constants/setup.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from setuptools import setup
|
| 2 |
+
setup(name='pyhwp.dev.constants',
|
| 3 |
+
py_modules=['pyhwp_dev_constants'],
|
| 4 |
+
entry_points={'zc.buildout': ['default = pyhwp_dev_constants:Recipe']})
|
tools/discover.lo/discover_lo.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import os.path
|
| 5 |
+
import sys
|
| 6 |
+
import logging
|
| 7 |
+
import contextlib
|
| 8 |
+
from discover_jre import executable_in_dir
|
| 9 |
+
from discover_jre import expose_options
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
logger = logging.getLogger(__name__)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def wellknown_locations():
|
| 16 |
+
if sys.platform == 'win32':
|
| 17 |
+
program_files = 'c:\\program files'
|
| 18 |
+
if os.path.exists(program_files):
|
| 19 |
+
for name in os.listdir(program_files):
|
| 20 |
+
yield dict(location=os.path.join(program_files, name))
|
| 21 |
+
if sys.platform.startswith('linux'):
|
| 22 |
+
yield dict(location='/usr/lib/libreoffice',
|
| 23 |
+
uno_python='/usr/bin/python') # Debian/Ubuntu
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def discover_lo(in_wellknown=True, in_path=True):
|
| 27 |
+
if in_wellknown:
|
| 28 |
+
for installation in discover_in_wellknown_locations():
|
| 29 |
+
yield installation
|
| 30 |
+
|
| 31 |
+
if in_path:
|
| 32 |
+
for installation in discover_in_path():
|
| 33 |
+
yield installation
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def discover_in_wellknown_locations():
|
| 37 |
+
for installation in wellknown_locations():
|
| 38 |
+
found = contains_program(installation['location'])
|
| 39 |
+
if found:
|
| 40 |
+
if 'uno_python' not in found and 'uno_python' in installation:
|
| 41 |
+
uno_python = python_import_uno(installation['uno_python'])
|
| 42 |
+
if uno_python:
|
| 43 |
+
found.update(resolve_uno_components(uno_python))
|
| 44 |
+
installation.update(found)
|
| 45 |
+
installation['through'] = 'WELLKNOWN_LOCATION'
|
| 46 |
+
yield installation
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def discover_in_path():
|
| 50 |
+
if 'PATH' in os.environ:
|
| 51 |
+
path = os.environ['PATH']
|
| 52 |
+
path = path.split(os.pathsep)
|
| 53 |
+
for dir in path:
|
| 54 |
+
libreoffice = contains_libreoffice(dir)
|
| 55 |
+
if libreoffice:
|
| 56 |
+
entry = dict(libreoffice=libreoffice, through='PATH')
|
| 57 |
+
|
| 58 |
+
# resolve symlinks
|
| 59 |
+
resolved = os.path.realpath(libreoffice)
|
| 60 |
+
location = os.path.dirname(os.path.dirname(resolved))
|
| 61 |
+
installation = contains_program(location)
|
| 62 |
+
if installation:
|
| 63 |
+
entry.update(installation)
|
| 64 |
+
|
| 65 |
+
# Debian/Ubuntu case
|
| 66 |
+
if 'uno' not in entry:
|
| 67 |
+
# try System python
|
| 68 |
+
uno_python = python_import_uno(sys.executable)
|
| 69 |
+
if uno_python:
|
| 70 |
+
entry.update(resolve_uno_components(uno_python))
|
| 71 |
+
|
| 72 |
+
yield entry
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def contains_libreoffice(dir):
|
| 76 |
+
return executable_in_dir('libreoffice', dir)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def contains_program(location):
|
| 80 |
+
program_dir = os.path.join(location, 'program')
|
| 81 |
+
if os.path.isdir(program_dir):
|
| 82 |
+
installation = dict(location=location, program=program_dir)
|
| 83 |
+
soffice = executable_in_dir('soffice', program_dir)
|
| 84 |
+
if soffice:
|
| 85 |
+
installation['soffice'] = soffice
|
| 86 |
+
unopkg = executable_in_dir('unopkg', program_dir)
|
| 87 |
+
if unopkg:
|
| 88 |
+
installation['unopkg'] = unopkg
|
| 89 |
+
|
| 90 |
+
program_python = executable_in_dir('python', program_dir)
|
| 91 |
+
if program_python:
|
| 92 |
+
uno_python = python_import_uno(program_python)
|
| 93 |
+
if uno_python:
|
| 94 |
+
installation.update(resolve_uno_components(uno_python))
|
| 95 |
+
|
| 96 |
+
basis_link = os.path.join(location, 'basis-link')
|
| 97 |
+
if os.path.islink(basis_link):
|
| 98 |
+
location = os.path.realpath(basis_link)
|
| 99 |
+
|
| 100 |
+
ure = find_ure(location)
|
| 101 |
+
if ure:
|
| 102 |
+
installation['ure'] = ure
|
| 103 |
+
|
| 104 |
+
return installation
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def find_ure(location):
|
| 108 |
+
ure_link = os.path.join(location, 'ure-link')
|
| 109 |
+
if os.path.islink(ure_link):
|
| 110 |
+
ure = os.path.realpath(ure_link)
|
| 111 |
+
if os.path.isdir(ure):
|
| 112 |
+
return ure
|
| 113 |
+
|
| 114 |
+
# win32
|
| 115 |
+
ure = os.path.join(location, 'ure')
|
| 116 |
+
if os.path.isdir(ure):
|
| 117 |
+
return ure
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def python_import_uno(python):
|
| 121 |
+
import subprocess
|
| 122 |
+
cmd = [python, '-c', 'import uno, unohelper']
|
| 123 |
+
ret = subprocess.call(cmd)
|
| 124 |
+
if ret == 0:
|
| 125 |
+
return python
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def resolve_uno_components(uno_python):
|
| 129 |
+
uno_python_core, modules = get_uno_locations(uno_python,
|
| 130 |
+
['uno', 'pyuno',
|
| 131 |
+
'unohelper'])
|
| 132 |
+
|
| 133 |
+
yield 'uno_python', uno_python
|
| 134 |
+
yield 'uno_python_core', uno_python_core
|
| 135 |
+
|
| 136 |
+
uno_pythonpath = set(os.path.dirname(modules[name])
|
| 137 |
+
for name in ['uno', 'unohelper'])
|
| 138 |
+
uno_pythonpath = os.pathsep.join(list(uno_pythonpath))
|
| 139 |
+
yield 'uno_pythonpath', uno_pythonpath
|
| 140 |
+
|
| 141 |
+
for name in modules:
|
| 142 |
+
yield name, modules[name]
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def get_uno_locations(python, modules):
|
| 146 |
+
statements = ['print __import__("sys").executable']
|
| 147 |
+
statements.extend('print __import__("%s").__file__' % name
|
| 148 |
+
for name in modules)
|
| 149 |
+
statements = ';'.join(statements)
|
| 150 |
+
cmd = [python, '-c', statements]
|
| 151 |
+
lines = subprocess_check_output(cmd)
|
| 152 |
+
lines = lines.strip()
|
| 153 |
+
lines = lines.split('\n')
|
| 154 |
+
return lines[0], dict(zip(modules, lines[1:]))
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def subprocess_check_output(cmd):
|
| 158 |
+
import tempfile
|
| 159 |
+
fd, name = tempfile.mkstemp()
|
| 160 |
+
f = os.fdopen(fd, 'r+')
|
| 161 |
+
try:
|
| 162 |
+
import subprocess
|
| 163 |
+
ret = subprocess.call(cmd, stdout=f)
|
| 164 |
+
if ret != 0:
|
| 165 |
+
logger.error('%d returned: %s', ret, ' '.join(cmd))
|
| 166 |
+
raise Exception('%s exit with %d' % (cmd[0], ret))
|
| 167 |
+
f.seek(0)
|
| 168 |
+
return f.read()
|
| 169 |
+
finally:
|
| 170 |
+
f.close()
|
| 171 |
+
os.unlink(name)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
LO_VARS = ('libreoffice'
|
| 175 |
+
' location program soffice unopkg'
|
| 176 |
+
' ure'
|
| 177 |
+
' uno_python uno_python_core uno_pythonpath uno pyuno unohelper').split(' ')
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def log_discovered(installations):
|
| 181 |
+
for installation in installations:
|
| 182 |
+
msg = 'discovered:'
|
| 183 |
+
for name in LO_VARS + ['through']:
|
| 184 |
+
if name in installation:
|
| 185 |
+
msg += ' ' + name + '=' + installation[name]
|
| 186 |
+
logger.info(msg)
|
| 187 |
+
yield installation
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
@contextlib.contextmanager
|
| 191 |
+
def original_pythonpath():
|
| 192 |
+
''' without buildout-modified environment variables
|
| 193 |
+
'''
|
| 194 |
+
if 'BUILDOUT_ORIGINAL_PYTHONPATH' in os.environ:
|
| 195 |
+
buildout_pythonpath = os.environ['PYTHONPATH']
|
| 196 |
+
os.environ['PYTHONPATH'] = os.environ.pop('BUILDOUT_ORIGINAL_PYTHONPATH')
|
| 197 |
+
yield
|
| 198 |
+
os.environ['BUILDOUT_ORIGINAL_PYTHONPATH'] = os.environ['PYTHONPATH']
|
| 199 |
+
os.environ['PYTHONPATH'] = buildout_pythonpath
|
| 200 |
+
else:
|
| 201 |
+
yield
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
class Discover(object):
|
| 205 |
+
''' Discover a LibreOffice installation and provide its location.
|
| 206 |
+
'''
|
| 207 |
+
|
| 208 |
+
def __init__(self, buildout, name, options):
|
| 209 |
+
self.__logger = logger = logging.getLogger(name)
|
| 210 |
+
for k, v in options.items():
|
| 211 |
+
logger.info('%s: %r', k, v)
|
| 212 |
+
|
| 213 |
+
self.__recipe = options['recipe']
|
| 214 |
+
self.__generate_stub = None
|
| 215 |
+
|
| 216 |
+
# special marker
|
| 217 |
+
not_found = options.get('not-found', 'not-found')
|
| 218 |
+
|
| 219 |
+
# expose platform-specific path seperator for convinience
|
| 220 |
+
options['pathsep'] = os.pathsep
|
| 221 |
+
|
| 222 |
+
if 'location' in options:
|
| 223 |
+
# if location is explicitly specified, it must contains java
|
| 224 |
+
# executable.
|
| 225 |
+
with original_pythonpath():
|
| 226 |
+
discovered = contains_program(options['location'])
|
| 227 |
+
if discovered:
|
| 228 |
+
# LO found, no further operation required.
|
| 229 |
+
expose_options(options, LO_VARS, discovered,
|
| 230 |
+
not_found=not_found, logger=logger)
|
| 231 |
+
return
|
| 232 |
+
from zc.buildout import UserError
|
| 233 |
+
raise UserError('LO not found at %s' % options['location'])
|
| 234 |
+
|
| 235 |
+
in_wellknown = options.get('search-in-wellknown-places',
|
| 236 |
+
'true').lower().strip()
|
| 237 |
+
in_wellknown = in_wellknown in ('true', 'yes', '1')
|
| 238 |
+
in_path = options.get('search-in-path', 'true').lower().strip()
|
| 239 |
+
in_path = in_path in ('true', 'yes', '1')
|
| 240 |
+
|
| 241 |
+
# location is not specified: try to discover a LO installation
|
| 242 |
+
with original_pythonpath():
|
| 243 |
+
discovered = discover_lo(in_wellknown, in_path)
|
| 244 |
+
discovered = log_discovered(discovered)
|
| 245 |
+
discovered = list(discovered)
|
| 246 |
+
|
| 247 |
+
if discovered:
|
| 248 |
+
discovered = discovered[0]
|
| 249 |
+
logger.info('following LO installation will be used:')
|
| 250 |
+
expose_options(options, LO_VARS, discovered, not_found=not_found,
|
| 251 |
+
logger=logger)
|
| 252 |
+
return
|
| 253 |
+
|
| 254 |
+
expose_options(options, LO_VARS, dict(), not_found=not_found,
|
| 255 |
+
logger=logger)
|
| 256 |
+
return
|
| 257 |
+
|
| 258 |
+
# no LO found: stub generation
|
| 259 |
+
parts_dir = buildout['buildout']['parts-directory']
|
| 260 |
+
self.__generate_stub = os.path.join(parts_dir, name)
|
| 261 |
+
options['location'] = self.__generate_stub
|
| 262 |
+
logger.info('LO not found: a dummy LO will be generated')
|
| 263 |
+
logger.info('location = %s (updating)', self.__generate_stub)
|
| 264 |
+
|
| 265 |
+
def install(self):
|
| 266 |
+
location = self.__generate_stub
|
| 267 |
+
if location is None:
|
| 268 |
+
return
|
| 269 |
+
|
| 270 |
+
if not os.path.exists(location):
|
| 271 |
+
os.makedirs(location)
|
| 272 |
+
yield location
|
| 273 |
+
self.__logger.info('A dummy LO has been generated: %s', location)
|
| 274 |
+
|
| 275 |
+
update = install
|
tools/discover.lo/setup.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from distutils.core import setup
|
| 2 |
+
setup(name='discover.lo',
|
| 3 |
+
py_modules=['discover_lo'],
|
| 4 |
+
entry_points={'zc.buildout': ['default = discover_lo:Discover']},
|
| 5 |
+
install_requires=['discover.jre'])
|
tools/discover.lxml/discover_lxml.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from __future__ import with_statement
|
| 3 |
+
import logging
|
| 4 |
+
import os
|
| 5 |
+
import os.path
|
| 6 |
+
import sys
|
| 7 |
+
from discover_python import expose_options
|
| 8 |
+
from discover_python import log_discovered
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
EXPOSE_NAMES = ('location', 'version')
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
FIND_SOURCE = '''
|
| 18 |
+
import os
|
| 19 |
+
import os.path
|
| 20 |
+
import sys
|
| 21 |
+
try:
|
| 22 |
+
from pkg_resources import get_distribution
|
| 23 |
+
except ImportError:
|
| 24 |
+
sys.stderr.write('pkg_resources is not found' + os.linesep)
|
| 25 |
+
try:
|
| 26 |
+
import lxml
|
| 27 |
+
except ImportError:
|
| 28 |
+
sys.stderr.write('lxml is not found' + os.linesep)
|
| 29 |
+
raise SystemExit(1)
|
| 30 |
+
else:
|
| 31 |
+
print(os.path.dirname(lxml.__path__[0]))
|
| 32 |
+
print('')
|
| 33 |
+
raise SystemExit(0)
|
| 34 |
+
|
| 35 |
+
try:
|
| 36 |
+
dist = get_distribution('lxml')
|
| 37 |
+
except Exception:
|
| 38 |
+
e = sys.exc_info()[1]
|
| 39 |
+
sys.stderr.write(repr(e))
|
| 40 |
+
sys.stderr.write(os.linesep)
|
| 41 |
+
raise SystemExit(1)
|
| 42 |
+
else:
|
| 43 |
+
print(dist.location)
|
| 44 |
+
print(dist.version)
|
| 45 |
+
'''
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def discover_lxml(executable):
|
| 49 |
+
import tempfile
|
| 50 |
+
fd, path = tempfile.mkstemp()
|
| 51 |
+
try:
|
| 52 |
+
with os.fdopen(fd, 'w') as f:
|
| 53 |
+
f.write(FIND_SOURCE)
|
| 54 |
+
|
| 55 |
+
from subprocess import Popen
|
| 56 |
+
from subprocess import PIPE
|
| 57 |
+
args = [executable, path]
|
| 58 |
+
env = dict(os.environ)
|
| 59 |
+
for k in ('PYTHONPATH', 'PYTHONHOME'):
|
| 60 |
+
if k in env:
|
| 61 |
+
del env[k]
|
| 62 |
+
try:
|
| 63 |
+
p = Popen(args, stdout=PIPE, env=env)
|
| 64 |
+
except Exception:
|
| 65 |
+
e = sys.exc_info()[1]
|
| 66 |
+
logger.exception(e)
|
| 67 |
+
return
|
| 68 |
+
else:
|
| 69 |
+
try:
|
| 70 |
+
lines = list(p.stdout)
|
| 71 |
+
finally:
|
| 72 |
+
p.wait()
|
| 73 |
+
finally:
|
| 74 |
+
os.unlink(path)
|
| 75 |
+
|
| 76 |
+
if p.returncode == 0:
|
| 77 |
+
location = lines[0].strip()
|
| 78 |
+
version = lines[1].strip()
|
| 79 |
+
yield dict(location=location,
|
| 80 |
+
version=version)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class DiscoverRecipe(object):
|
| 84 |
+
''' Discover lxml and provide its location.
|
| 85 |
+
'''
|
| 86 |
+
|
| 87 |
+
def __init__(self, buildout, name, options):
|
| 88 |
+
self.__logger = logger = logging.getLogger(name)
|
| 89 |
+
for k, v in options.items():
|
| 90 |
+
logger.info('%s: %r', k, v)
|
| 91 |
+
|
| 92 |
+
self.__recipe = options['recipe']
|
| 93 |
+
|
| 94 |
+
not_found = options.get('not-found', 'not-found')
|
| 95 |
+
executable = options.get('python', 'python').strip()
|
| 96 |
+
#version = options.get('version', '').strip()
|
| 97 |
+
|
| 98 |
+
founds = discover_lxml(executable=executable)
|
| 99 |
+
founds = log_discovered('matching', founds, EXPOSE_NAMES,
|
| 100 |
+
log=logger.info)
|
| 101 |
+
founds = list(founds)
|
| 102 |
+
|
| 103 |
+
# location is not specified: try to discover a Python installation
|
| 104 |
+
if founds:
|
| 105 |
+
found = founds[0]
|
| 106 |
+
logger.info('the first-found one will be used:')
|
| 107 |
+
expose_options(options, EXPOSE_NAMES, found,
|
| 108 |
+
not_found=not_found, logger=logger)
|
| 109 |
+
return
|
| 110 |
+
|
| 111 |
+
# ensure executable publishes not-found marker
|
| 112 |
+
expose_options(options, ['location'], dict(), not_found=not_found,
|
| 113 |
+
logger=logger)
|
| 114 |
+
logger.warning('lxml not found')
|
| 115 |
+
return
|
| 116 |
+
|
| 117 |
+
def install(self):
|
| 118 |
+
return []
|
| 119 |
+
|
| 120 |
+
update = install
|
tools/discover.lxml/setup.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import os
|
| 3 |
+
import os.path
|
| 4 |
+
from distutils.core import setup
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def main():
|
| 8 |
+
cwd = os.getcwd()
|
| 9 |
+
setupdir = os.path.dirname(os.path.abspath(__file__))
|
| 10 |
+
os.chdir(setupdir)
|
| 11 |
+
try:
|
| 12 |
+
setup(name='discover.lxml',
|
| 13 |
+
py_modules=['discover_lxml'],
|
| 14 |
+
install_requires=['discover.python'],
|
| 15 |
+
entry_points={'zc.buildout':
|
| 16 |
+
['default = discover_lxml:DiscoverRecipe']})
|
| 17 |
+
finally:
|
| 18 |
+
os.chdir(cwd)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
if __name__ == '__main__':
|
| 22 |
+
main()
|
tools/discover.python/discover_python.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
import os
|
| 5 |
+
import os.path
|
| 6 |
+
import sys
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
EXPOSE_NAMES = ('executable', 'version', 'prefix', 'exec_prefix', 'through')
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def wellknown_locations():
|
| 17 |
+
if sys.platform == 'win32':
|
| 18 |
+
base = 'c:\\'
|
| 19 |
+
for name in os.listdir(base):
|
| 20 |
+
name = name.lower()
|
| 21 |
+
if name.startswith('python'):
|
| 22 |
+
shortversion = name[len('python'):]
|
| 23 |
+
m = re.match('[23][0-9]', shortversion)
|
| 24 |
+
if m:
|
| 25 |
+
yield base + name
|
| 26 |
+
elif 'PYTHONZ_ROOT' in os.environ:
|
| 27 |
+
pythonz_root = os.environ['PYTHONZ_ROOT']
|
| 28 |
+
pythons = os.path.join(pythonz_root, 'pythons')
|
| 29 |
+
for item in os.listdir(pythons):
|
| 30 |
+
yield os.path.join(pythons, item)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def discover_python(in_wellknown=True, in_path=True):
|
| 34 |
+
|
| 35 |
+
if in_wellknown:
|
| 36 |
+
for found in search_in_wellknown_locations():
|
| 37 |
+
yield found
|
| 38 |
+
|
| 39 |
+
if in_path:
|
| 40 |
+
for found in search_in_path():
|
| 41 |
+
yield found
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def search_in_wellknown_locations():
|
| 45 |
+
for location in wellknown_locations():
|
| 46 |
+
if sys.platform == 'win32':
|
| 47 |
+
founds = contains_python(location)
|
| 48 |
+
else:
|
| 49 |
+
founds = contains_python_in_bin(location)
|
| 50 |
+
|
| 51 |
+
for found in founds:
|
| 52 |
+
found['through'] = 'WELLKNOWN_LOCATION'
|
| 53 |
+
yield found
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def search_in_path():
|
| 57 |
+
if 'PATH' in os.environ:
|
| 58 |
+
path = os.environ['PATH']
|
| 59 |
+
path = path.split(os.pathsep)
|
| 60 |
+
for dir in path:
|
| 61 |
+
for found in contains_python(dir):
|
| 62 |
+
found['through'] = 'PATH'
|
| 63 |
+
|
| 64 |
+
# resolve symlinks
|
| 65 |
+
resolved = os.path.realpath(found['executable'])
|
| 66 |
+
found['executable'] = resolved
|
| 67 |
+
yield found
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def contains_python_in_bin(dir):
|
| 71 |
+
bindir = os.path.join(dir, 'bin')
|
| 72 |
+
return contains_python(bindir)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def contains_python(dir):
|
| 76 |
+
vers = {
|
| 77 |
+
2: [3, 4, 5, 6, 7],
|
| 78 |
+
3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
| 79 |
+
}
|
| 80 |
+
names = (('python%d.%d' % (major, minor))
|
| 81 |
+
for major in reversed(sorted(vers))
|
| 82 |
+
for minor in reversed(sorted(vers[major])))
|
| 83 |
+
names = list(names) + ['python']
|
| 84 |
+
for name in names:
|
| 85 |
+
executable = executable_in_dir(name, dir)
|
| 86 |
+
if executable:
|
| 87 |
+
found = executable_is_python(executable)
|
| 88 |
+
if found:
|
| 89 |
+
yield found
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def executable_in_dir(name, dir):
|
| 93 |
+
assert name == os.path.basename(name)
|
| 94 |
+
if sys.platform == 'win32':
|
| 95 |
+
name += '.exe'
|
| 96 |
+
path = os.path.join(dir, name)
|
| 97 |
+
if not os.path.exists(path):
|
| 98 |
+
return
|
| 99 |
+
return path
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def executable_is_python(executable):
|
| 103 |
+
from subprocess import Popen
|
| 104 |
+
from subprocess import PIPE
|
| 105 |
+
cmd = '''
|
| 106 |
+
import os, sys
|
| 107 |
+
print(sys.hexversion)
|
| 108 |
+
print(os.pathsep.join([sys.prefix, sys.exec_prefix]))
|
| 109 |
+
'''.strip().replace('\n', ';')
|
| 110 |
+
args = [executable, '-c', cmd]
|
| 111 |
+
env = dict(os.environ)
|
| 112 |
+
for k in ('PYTHONPATH', 'PYTHONHOME'):
|
| 113 |
+
if k in env:
|
| 114 |
+
del env[k]
|
| 115 |
+
try:
|
| 116 |
+
p = Popen(args, stdout=PIPE, env=env)
|
| 117 |
+
lines = p.stdout.read().split('\n')
|
| 118 |
+
p.wait()
|
| 119 |
+
ver = int(lines[0])
|
| 120 |
+
ver_major = str(ver >> 24 & 0xff)
|
| 121 |
+
ver_minor = str(ver >> 16 & 0xff)
|
| 122 |
+
ver_patch = str(ver >> 8 & 0xff)
|
| 123 |
+
ver = ver_major, ver_minor, ver_patch
|
| 124 |
+
version = '.'.join(ver)
|
| 125 |
+
prefix, exec_prefix = lines[1].split(os.pathsep)
|
| 126 |
+
return dict(executable=executable, version=version,
|
| 127 |
+
prefix=prefix, exec_prefix=exec_prefix)
|
| 128 |
+
except Exception, e:
|
| 129 |
+
logger.error('popen failed: %s', args)
|
| 130 |
+
logger.exception(e)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def log_discovered(qualify, found, names, log=logger.info):
|
| 134 |
+
for item in found:
|
| 135 |
+
msg = qualify + ':'
|
| 136 |
+
for name in names:
|
| 137 |
+
if name in item:
|
| 138 |
+
msg += ' %s=%s' % (name, item[name])
|
| 139 |
+
log(msg)
|
| 140 |
+
yield item
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def expose_options(options, names, found, not_found, logger=logger):
|
| 144 |
+
for name in names:
|
| 145 |
+
if name in found:
|
| 146 |
+
value = found[name]
|
| 147 |
+
if name in options:
|
| 148 |
+
if value != options[name]:
|
| 149 |
+
logger.info('(updating) %s = %s', name, value)
|
| 150 |
+
options[name] = value
|
| 151 |
+
else:
|
| 152 |
+
logger.info('(preserving) %s = %s', name, value)
|
| 153 |
+
else:
|
| 154 |
+
logger.info('(exposing) %s = %s', name, value)
|
| 155 |
+
options[name] = value
|
| 156 |
+
else:
|
| 157 |
+
if name not in options:
|
| 158 |
+
options[name] = value = not_found
|
| 159 |
+
logger.info('(exposing) %s = %s', name, value)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
class Discover(object):
|
| 163 |
+
''' Discover Python and provide its location.
|
| 164 |
+
'''
|
| 165 |
+
|
| 166 |
+
def __init__(self, buildout, name, options):
|
| 167 |
+
from zc.buildout import UserError
|
| 168 |
+
self.__logger = logger = logging.getLogger(name)
|
| 169 |
+
for k, v in options.items():
|
| 170 |
+
logger.info('%s: %r', k, v)
|
| 171 |
+
|
| 172 |
+
self.__recipe = options['recipe']
|
| 173 |
+
|
| 174 |
+
not_found = options.get('not-found', 'not-found')
|
| 175 |
+
version = options.get('version', '').strip()
|
| 176 |
+
|
| 177 |
+
if 'location' in options:
|
| 178 |
+
# if location is explicitly specified, it must contains java
|
| 179 |
+
# executable.
|
| 180 |
+
for found in contains_python_in_bin(options['location']):
|
| 181 |
+
if not version or found['version'].startswith(version):
|
| 182 |
+
# Python found, no further discovery required.
|
| 183 |
+
options['executable'] = found['executable']
|
| 184 |
+
return
|
| 185 |
+
raise UserError('Python not found at %s' % options['location'])
|
| 186 |
+
|
| 187 |
+
in_wellknown = options.get('search-in-wellknown-places',
|
| 188 |
+
'true').lower().strip()
|
| 189 |
+
in_wellknown = in_wellknown in ('true', 'yes', '1')
|
| 190 |
+
in_path = options.get('search-in-path', 'true').lower().strip()
|
| 191 |
+
in_path = in_path in ('true', 'yes', '1')
|
| 192 |
+
|
| 193 |
+
founds = discover_python(in_wellknown=in_wellknown,
|
| 194 |
+
in_path=in_path)
|
| 195 |
+
founds = log_discovered('candidates', founds, EXPOSE_NAMES,
|
| 196 |
+
log=logger.debug)
|
| 197 |
+
if version:
|
| 198 |
+
# filter with version
|
| 199 |
+
founds = (found for found in founds
|
| 200 |
+
if found['version'].startswith(version))
|
| 201 |
+
founds = log_discovered('matching', founds, EXPOSE_NAMES,
|
| 202 |
+
log=logger.info)
|
| 203 |
+
founds = list(founds)
|
| 204 |
+
|
| 205 |
+
# location is not specified: try to discover a Python installation
|
| 206 |
+
if founds:
|
| 207 |
+
found = founds[0]
|
| 208 |
+
logger.info('the first-matching one will be used:')
|
| 209 |
+
expose_options(options, EXPOSE_NAMES, found,
|
| 210 |
+
not_found=not_found, logger=logger)
|
| 211 |
+
return
|
| 212 |
+
|
| 213 |
+
# ensure executable publishes not-found marker
|
| 214 |
+
expose_options(options, ['executable'], dict(), not_found=not_found,
|
| 215 |
+
logger=logger)
|
| 216 |
+
logger.warning('Python not found')
|
| 217 |
+
return
|
| 218 |
+
|
| 219 |
+
def install(self):
|
| 220 |
+
return []
|
| 221 |
+
|
| 222 |
+
update = install
|
tools/discover.python/setup.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import os
|
| 3 |
+
import os.path
|
| 4 |
+
from distutils.core import setup
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
cwd = os.getcwd()
|
| 8 |
+
setupdir = os.path.dirname(os.path.abspath(__file__))
|
| 9 |
+
os.chdir(setupdir)
|
| 10 |
+
try:
|
| 11 |
+
setup(name='discover.python',
|
| 12 |
+
py_modules=['discover_python'],
|
| 13 |
+
entry_points={'zc.buildout': ['default = discover_python:Discover']})
|
| 14 |
+
finally:
|
| 15 |
+
os.chdir(cwd)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
if __name__ == '__main__':
|
| 19 |
+
main()
|
tools/download/pyhwp_download.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import sys
|
| 3 |
+
import urlparse
|
| 4 |
+
import os.path
|
| 5 |
+
import logging
|
| 6 |
+
from binascii import a2b_hex
|
| 7 |
+
from binascii import b2a_hex
|
| 8 |
+
from hashlib import md5
|
| 9 |
+
|
| 10 |
+
import requests
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
logging.basicConfig()
|
| 18 |
+
logger.setLevel(logging.DEBUG)
|
| 19 |
+
|
| 20 |
+
url = sys.argv[1]
|
| 21 |
+
dst = sys.argv[2]
|
| 22 |
+
md5_given = a2b_hex(sys.argv[3])
|
| 23 |
+
result = urlparse.urlparse(url)
|
| 24 |
+
|
| 25 |
+
filename = os.path.basename(result.path)
|
| 26 |
+
|
| 27 |
+
if not os.path.exists(dst):
|
| 28 |
+
destination_path = dst
|
| 29 |
+
logger.debug('%s not exists: destination=%s', dst, destination_path)
|
| 30 |
+
else:
|
| 31 |
+
if os.path.isdir(dst):
|
| 32 |
+
destination_path = os.path.join(dst, filename)
|
| 33 |
+
logger.debug('%s is a directory: destination=%s', dst,
|
| 34 |
+
destination_path)
|
| 35 |
+
else:
|
| 36 |
+
destination_path = dst
|
| 37 |
+
|
| 38 |
+
if os.path.exists(destination_path):
|
| 39 |
+
md5_existing = md5_file(destination_path)
|
| 40 |
+
if md5_given == md5_existing:
|
| 41 |
+
logger.debug('%s exists: skipped', destination_path)
|
| 42 |
+
return
|
| 43 |
+
|
| 44 |
+
response = requests.get(url, stream=True)
|
| 45 |
+
response.raise_for_status()
|
| 46 |
+
with open(destination_path, 'wb') as f:
|
| 47 |
+
copy_stream(response.raw, f)
|
| 48 |
+
|
| 49 |
+
md5_downloaded = md5_file(destination_path)
|
| 50 |
+
if md5_given != md5_downloaded:
|
| 51 |
+
logger.error('md5 not match: %s', b2a_hex(md5_downloaded))
|
| 52 |
+
raise SystemExit(1)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def copy_stream(src, dst):
|
| 56 |
+
while True:
|
| 57 |
+
data = src.read(16384)
|
| 58 |
+
if len(data) == 0:
|
| 59 |
+
break
|
| 60 |
+
dst.write(data)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def md5_file(path):
|
| 64 |
+
with open(path, 'rb') as f:
|
| 65 |
+
m = md5('')
|
| 66 |
+
while True:
|
| 67 |
+
data = f.read(16384)
|
| 68 |
+
if len(data) == 0:
|
| 69 |
+
break
|
| 70 |
+
m.update(data)
|
| 71 |
+
return m.digest()
|
tools/download/setup.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from setuptools import setup
|
| 3 |
+
setup(name='pyhwp.develop.download',
|
| 4 |
+
py_modules=['pyhwp_download'],
|
| 5 |
+
install_requires=['requests'],
|
| 6 |
+
entry_points=dict(console_scripts=['pyhwp-download = pyhwp_download:main']))
|
tools/egg.path/egg_path.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class EggPath(object):
|
| 6 |
+
def __init__(self, buildout, name, options):
|
| 7 |
+
self.__name = name
|
| 8 |
+
self.__logger = logging.getLogger(name)
|
| 9 |
+
|
| 10 |
+
eggs = options['eggs']
|
| 11 |
+
eggs = eggs.split('\n')
|
| 12 |
+
eggs = list(egg.strip() for egg in eggs)
|
| 13 |
+
for egg in eggs:
|
| 14 |
+
self.__logger.info('egg: %s', egg)
|
| 15 |
+
|
| 16 |
+
from zc.recipe.egg.egg import Eggs
|
| 17 |
+
eggs_recipe = Eggs(buildout, name, options)
|
| 18 |
+
req, ws = eggs_recipe.working_set()
|
| 19 |
+
for dist in ws:
|
| 20 |
+
self.__logger.debug('dist: %s %s at %s', dist, dist.key, dist.location)
|
| 21 |
+
dist_locations = dict((dist.key, dist.location) for dist in ws)
|
| 22 |
+
egg_path = list(dist_locations[egg] for egg in eggs)
|
| 23 |
+
for p in egg_path:
|
| 24 |
+
self.__logger.info('egg-path: %s', p)
|
| 25 |
+
options['egg-path'] = ' '.join(egg_path)
|
| 26 |
+
|
| 27 |
+
def install(self):
|
| 28 |
+
return []
|
| 29 |
+
|
| 30 |
+
update = install
|
tools/egg.path/setup.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from setuptools import setup
|
| 2 |
+
setup(name='egg.path',
|
| 3 |
+
install_requires=['zc.recipe.egg'],
|
| 4 |
+
py_modules=['egg_path'],
|
| 5 |
+
entry_points = {
|
| 6 |
+
'zc.buildout': [
|
| 7 |
+
'default = egg_path:EggPath'
|
| 8 |
+
]
|
| 9 |
+
})
|
tools/gpl/gpl/Pysec.py
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
# originally authored by Peter Thatcher, Public Domain
|
| 3 |
+
# See http://www.valuedlessons.com/2008/02/pysec-monadic-combinatoric-parsing-in.html
|
| 4 |
+
|
| 5 |
+
def Record(*props):
|
| 6 |
+
class cls(RecordBase):
|
| 7 |
+
pass
|
| 8 |
+
|
| 9 |
+
cls.setProps(props)
|
| 10 |
+
|
| 11 |
+
return cls
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class RecordBase(tuple):
|
| 15 |
+
PROPS = ()
|
| 16 |
+
|
| 17 |
+
def __new__(cls, *values):
|
| 18 |
+
if cls.prepare != RecordBase.prepare:
|
| 19 |
+
values = cls.prepare(*values)
|
| 20 |
+
return cls.fromValues(values)
|
| 21 |
+
|
| 22 |
+
@classmethod
|
| 23 |
+
def fromValues(cls, values):
|
| 24 |
+
return tuple.__new__(cls, values)
|
| 25 |
+
|
| 26 |
+
def __repr__(self):
|
| 27 |
+
return self.__class__.__name__ + tuple.__repr__(self)
|
| 28 |
+
|
| 29 |
+
## overridable
|
| 30 |
+
@classmethod
|
| 31 |
+
def prepare(cls, *args):
|
| 32 |
+
return args
|
| 33 |
+
|
| 34 |
+
## setting up getters and setters
|
| 35 |
+
@classmethod
|
| 36 |
+
def setProps(cls, props):
|
| 37 |
+
for index, prop in enumerate(props):
|
| 38 |
+
cls.setProp(index, prop)
|
| 39 |
+
cls.PROPS = props
|
| 40 |
+
|
| 41 |
+
@classmethod
|
| 42 |
+
def setProp(cls, index, prop):
|
| 43 |
+
getter_name = prop
|
| 44 |
+
setter_name = "set" + prop[0].upper() + prop[1:]
|
| 45 |
+
|
| 46 |
+
setattr(cls, getter_name, cls.makeGetter(index, prop))
|
| 47 |
+
setattr(cls, setter_name, cls.makeSetter(index, prop))
|
| 48 |
+
|
| 49 |
+
@classmethod
|
| 50 |
+
def makeGetter(cls, index, prop):
|
| 51 |
+
return property(fget = lambda self : self[index])
|
| 52 |
+
|
| 53 |
+
@classmethod
|
| 54 |
+
def makeSetter(cls, index, prop):
|
| 55 |
+
def setter(self, value):
|
| 56 |
+
values = (value if current_index == index
|
| 57 |
+
else current_value
|
| 58 |
+
for current_index, current_value
|
| 59 |
+
in enumerate(self))
|
| 60 |
+
return self.fromValues(values)
|
| 61 |
+
return setter
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class ByteStream(Record("bytes", "index")):
|
| 65 |
+
@classmethod
|
| 66 |
+
def prepare(cls, bytes, index = 0):
|
| 67 |
+
return (bytes, index)
|
| 68 |
+
|
| 69 |
+
def get(self, count):
|
| 70 |
+
start = self.index
|
| 71 |
+
end = start + count
|
| 72 |
+
bytes = self.bytes[start : end]
|
| 73 |
+
return bytes, (self.setIndex(end) if bytes else self)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def make_decorator(func, *dec_args):
|
| 77 |
+
def decorator(undecorated):
|
| 78 |
+
def decorated(*args, **kargs):
|
| 79 |
+
return func(undecorated, args, kargs, *dec_args)
|
| 80 |
+
|
| 81 |
+
decorated.__name__ = undecorated.__name__
|
| 82 |
+
return decorated
|
| 83 |
+
|
| 84 |
+
decorator.__name__ = func.__name__
|
| 85 |
+
return decorator
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
decorator = make_decorator
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class Monad:
|
| 92 |
+
## Must be overridden
|
| 93 |
+
def bind(self, func):
|
| 94 |
+
raise NotImplementedError
|
| 95 |
+
|
| 96 |
+
@classmethod
|
| 97 |
+
def unit(cls, val):
|
| 98 |
+
raise NotImplementedError
|
| 99 |
+
|
| 100 |
+
@classmethod
|
| 101 |
+
def lift(cls, func):
|
| 102 |
+
return (lambda val : cls.unit(func(val)))
|
| 103 |
+
|
| 104 |
+
## useful defaults that should probably NOT be overridden
|
| 105 |
+
def __rshift__(self, bindee):
|
| 106 |
+
return self.bind(bindee)
|
| 107 |
+
|
| 108 |
+
def __and__(self, monad):
|
| 109 |
+
return self.shove(monad)
|
| 110 |
+
|
| 111 |
+
## could be overridden if useful or if more efficient
|
| 112 |
+
def shove(self, monad):
|
| 113 |
+
return self.bind(lambda _ : monad)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
class StateChanger(Record("changer", "bindees"), Monad):
|
| 117 |
+
@classmethod
|
| 118 |
+
def prepare(cls, changer, bindees = ()):
|
| 119 |
+
return (changer, bindees)
|
| 120 |
+
|
| 121 |
+
# binding can be slow since it happens at bind time rather than at run time
|
| 122 |
+
def bind(self, bindee):
|
| 123 |
+
return self.setBindees(self.bindees + (bindee,))
|
| 124 |
+
|
| 125 |
+
def __call__(self, state):
|
| 126 |
+
return self.run(state)
|
| 127 |
+
|
| 128 |
+
def run(self, state0):
|
| 129 |
+
value, state = self.changer(state0) if callable(self.changer) else self.changer
|
| 130 |
+
state = state0 if state is None else state
|
| 131 |
+
|
| 132 |
+
for bindee in self.bindees:
|
| 133 |
+
value, state = bindee(value).run(state)
|
| 134 |
+
return (value, state)
|
| 135 |
+
|
| 136 |
+
@classmethod
|
| 137 |
+
def unit(cls, value):
|
| 138 |
+
return cls((value, None))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
######## Parser Monad ###########
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class ParserState(Record("stream", "position")):
|
| 145 |
+
@classmethod
|
| 146 |
+
def prepare(cls, stream, position = 0):
|
| 147 |
+
return (stream, position)
|
| 148 |
+
|
| 149 |
+
def read(self, count):
|
| 150 |
+
collection, stream = self.stream.get(count)
|
| 151 |
+
return collection, self.fromValues((stream, self.position + count))
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
class Parser(StateChanger):
|
| 155 |
+
def parseString(self, bytes):
|
| 156 |
+
return self.parseStream(ByteStream(bytes))
|
| 157 |
+
|
| 158 |
+
def parseStream(self, stream):
|
| 159 |
+
state = ParserState(stream)
|
| 160 |
+
value, state = self.run(state)
|
| 161 |
+
return value
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
class ParseFailed(Exception):
|
| 165 |
+
def __init__(self, message, state):
|
| 166 |
+
self.message = message
|
| 167 |
+
self.state = state
|
| 168 |
+
Exception.__init__(self, message)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@decorator
|
| 172 |
+
def parser(func, func_args, func_kargs):
|
| 173 |
+
def changer(state):
|
| 174 |
+
return func(state, *func_args, **func_kargs)
|
| 175 |
+
changer.__name__ = func.__name__
|
| 176 |
+
return Parser(changer)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
##### combinatoric functions #########
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
@parser
|
| 183 |
+
def tokens(state0, count, process):
|
| 184 |
+
tokens, state1 = state0.read(count)
|
| 185 |
+
|
| 186 |
+
passed, value = process(tokens)
|
| 187 |
+
if passed:
|
| 188 |
+
return (value, state1)
|
| 189 |
+
else:
|
| 190 |
+
raise ParseFailed(value, state0)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def read(count):
|
| 194 |
+
return tokens(count, lambda values : (True, values))
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
@parser
|
| 198 |
+
def skip(state0, parser):
|
| 199 |
+
value, state1 = parser(state0)
|
| 200 |
+
return (None, state1)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
@parser
|
| 204 |
+
def option(state, default_value, parser):
|
| 205 |
+
try:
|
| 206 |
+
return parser(state)
|
| 207 |
+
except ParseFailed, failure:
|
| 208 |
+
if failure.state == state:
|
| 209 |
+
return (default_value, state)
|
| 210 |
+
else:
|
| 211 |
+
raise
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
@parser
|
| 215 |
+
def choice(state, parsers):
|
| 216 |
+
for parser in parsers:
|
| 217 |
+
try:
|
| 218 |
+
return parser(state)
|
| 219 |
+
except ParseFailed, failure:
|
| 220 |
+
if failure.state != state:
|
| 221 |
+
raise failure
|
| 222 |
+
raise ParseFailed("no choices were found", state)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
@parser
|
| 226 |
+
def match(state0, expected):
|
| 227 |
+
actual, state1 = read(len(expected))(state0)
|
| 228 |
+
if actual == expected:
|
| 229 |
+
return actual, state1
|
| 230 |
+
else:
|
| 231 |
+
raise ParseFailed("expected %r, actual %r" % (expected, actual), state0)
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
def between(before, inner, after):
|
| 235 |
+
return before & inner >> (lambda value : after & Parser.unit(value))
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def quoted(before, inner, after):
|
| 239 |
+
return between(match(before), inner, match(after))
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def quoted_collection(start, space, inner, joiner, end):
|
| 243 |
+
return quoted(start, space & sep_end_by(inner, joiner), end)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
@parser
|
| 247 |
+
def many(state, parser, min_count = 0):
|
| 248 |
+
values = []
|
| 249 |
+
|
| 250 |
+
try:
|
| 251 |
+
while True:
|
| 252 |
+
value, state = parser(state)
|
| 253 |
+
values.append(value)
|
| 254 |
+
except ParseFailed:
|
| 255 |
+
if len(values) < min_count:
|
| 256 |
+
raise
|
| 257 |
+
|
| 258 |
+
return values, state
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
@parser
|
| 262 |
+
def group(state, parsers):
|
| 263 |
+
values = []
|
| 264 |
+
|
| 265 |
+
for parser in parsers:
|
| 266 |
+
value, state = parser(state)
|
| 267 |
+
values.append(value)
|
| 268 |
+
|
| 269 |
+
return values, state
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def pair(parser1, parser2):
|
| 273 |
+
# return group((parser1, parser2))
|
| 274 |
+
return parser1 >> (lambda value1 : parser2 >> (lambda value2 : Parser.unit((value1, value2))))
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
@parser
|
| 278 |
+
def skip_many(state, parser):
|
| 279 |
+
try:
|
| 280 |
+
while True:
|
| 281 |
+
value, state = parser(state)
|
| 282 |
+
except ParseFailed:
|
| 283 |
+
return (None, state)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def skip_before(before, parser):
|
| 287 |
+
return skip(before) & parser
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
@parser
|
| 291 |
+
def skip_after(state0, parser, after):
|
| 292 |
+
value, state1 = parser(state0)
|
| 293 |
+
_, state2 = after(state1)
|
| 294 |
+
return value, state2
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
@parser
|
| 298 |
+
def option_many(state0, first, repeated, min_count = 0):
|
| 299 |
+
try:
|
| 300 |
+
first_value, state1 = first(state0)
|
| 301 |
+
except ParseFailed:
|
| 302 |
+
if min_count > 0:
|
| 303 |
+
raise
|
| 304 |
+
else:
|
| 305 |
+
return [], state0
|
| 306 |
+
else:
|
| 307 |
+
values, state2 = many(repeated, min_count-1)(state1)
|
| 308 |
+
values.insert(0, first_value)
|
| 309 |
+
return values, state2
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
# parser separated and ended by sep
|
| 313 |
+
def end_by(parser, sep_parser, min_count = 0):
|
| 314 |
+
return many(skip_after(parser, sep_parser), min_count)
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
# parser separated by sep
|
| 318 |
+
def sep_by(parser, sep_parser, min_count = 0):
|
| 319 |
+
return option_many(parser, skip_before(sep_parser, parser), min_count)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
# parser separated and optionally ended by sep
|
| 323 |
+
def sep_end_by(parser, sep_parser, min_count = 0):
|
| 324 |
+
return skip_after(sep_by(parser, sep_parser, min_count), option(None, sep_parser))
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
##### char-specific parsing ###########
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def satisfy(name, passes):
|
| 331 |
+
return tokens(1, lambda char : (True, char) if passes(char) else (False, "not " + name))
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def one_of(chars):
|
| 335 |
+
char_set = frozenset(chars)
|
| 336 |
+
return satisfy("one of %r" % chars, lambda char : char in char_set)
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def none_of(chars):
|
| 340 |
+
char_set = frozenset(chars)
|
| 341 |
+
return satisfy("not one of %r" % chars, lambda char : char and char not in char_set)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def maybe_match_parser(parser):
|
| 345 |
+
return match(parser) if isinstance(parser, str) else parser
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
def maybe_match_parsers(parsers):
|
| 349 |
+
return tuple(maybe_match_parser(parser) for parser in parsers)
|
| 350 |
+
|
| 351 |
+
|
| 352 |
+
def many_chars(parser, min_count = 0):
|
| 353 |
+
return join_chars(many(parser, min_count))
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
def option_chars(parsers):
|
| 357 |
+
return option("", group_chars(parsers))
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def group_chars(parsers):
|
| 361 |
+
return join_chars(group(maybe_match_parsers(parsers)))
|
| 362 |
+
#return join_chars(group(parsers))
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def join_chars(parser):
|
| 366 |
+
return parser >> Parser.lift("".join)
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
def while_one_of(chars, min_count = 0):
|
| 370 |
+
return many_chars(one_of(chars), min_count)
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
def until_one_of(chars, min_count = 0):
|
| 374 |
+
return many_chars(none_of(chars), min_count)
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
def char_range(begin, end):
|
| 378 |
+
return "".join(chr(num) for num in xrange(ord(begin), ord(end)))
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def quoted_chars(start, end):
|
| 382 |
+
assert len(end) == 1, "end string must be exactly 1 character"
|
| 383 |
+
return quoted(start, many_chars(none_of(end)), end)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
digit = one_of(char_range("0", "9"))
|
| 387 |
+
digits = many_chars(digit, min_count = 1)
|
| 388 |
+
space = one_of(" \v\f\t\r\n")
|
| 389 |
+
spaces = skip_many(space)
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
############# simplified JSON ########################
|
| 393 |
+
|
| 394 |
+
#from Pysec import Parser, choice, quoted_chars, group_chars, option_chars, digits, between, pair, spaces, match, quoted_collection, sep_end_by
|
| 395 |
+
|
| 396 |
+
#HACK: json_choices is used to get around mutual recursion
|
| 397 |
+
#a json is value is one of text, number, mapping, and collection, which we define later
|
| 398 |
+
json_choices = []
|
| 399 |
+
json = choice(json_choices)
|
| 400 |
+
|
| 401 |
+
#text is any characters between quotes
|
| 402 |
+
text = quoted_chars("'", "'")
|
| 403 |
+
|
| 404 |
+
#sort of like the regular expression -?[0-9]+(\.[0-9]+)?
|
| 405 |
+
#in case you're unfamiliar with monads, "parser >> Parser.lift(func)" means "pass the parsed value into func but give me a new Parser back"
|
| 406 |
+
number = group_chars([option_chars(["-"]), digits, option_chars([".", digits])]) >> Parser.lift(float)
|
| 407 |
+
|
| 408 |
+
#quoted_collection(start, space, inner, joiner, end) means "a list of inner separated by joiner surrounded by start and end"
|
| 409 |
+
#also, we have to put a lot of spaces in there since JSON allows lot of optional whitespace
|
| 410 |
+
joiner = between(spaces, match(","), spaces)
|
| 411 |
+
mapping_pair = pair(text, spaces & match(":") & spaces & json)
|
| 412 |
+
collection = quoted_collection("[", spaces, json, joiner, "]") >> Parser.lift(list)
|
| 413 |
+
mapping = quoted_collection("{", spaces, mapping_pair, joiner, "}") >> Parser.lift(dict)
|
| 414 |
+
|
| 415 |
+
#HACK: finish the work around mutual recursion
|
| 416 |
+
json_choices.extend([text, number, mapping, collection])
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
############# simplified CSV ########################
|
| 420 |
+
|
| 421 |
+
def line(cell):
|
| 422 |
+
return sep_end_by(cell, match(","))
|
| 423 |
+
|
| 424 |
+
def csv(cell):
|
| 425 |
+
return sep_end_by(line(cell), match("\n"))
|
| 426 |
+
|
| 427 |
+
############# testing ####################
|
| 428 |
+
|
| 429 |
+
if __name__ == '__main__':
|
| 430 |
+
print json.parseString("{'a' : -1.0, 'b' : 2.0, 'z' : {'c' : [1.0, [2.0, [3.0]]]}}")
|
| 431 |
+
print csv(number).parseString("1,2,3\n4,5,6")
|
| 432 |
+
print csv(json).parseString("{'a' : 'A'},[1, 2, 3],'zzz'\n-1.0,2.0,-3.0")
|
tools/gpl/gpl/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
'''
|
| 3 |
+
Usage:
|
| 4 |
+
gpl [options] <files>...
|
| 5 |
+
gpl -h | --help
|
| 6 |
+
gpl --version
|
| 7 |
+
|
| 8 |
+
Options:
|
| 9 |
+
-h --help Show this screen
|
| 10 |
+
--version Show version
|
| 11 |
+
--add-year=<year> Add release year
|
| 12 |
+
--set-author=<author> Set author
|
| 13 |
+
'''
|
| 14 |
+
import logging
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
logger = logging.getLogger(__name__)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def main():
|
| 21 |
+
from docopt import docopt
|
| 22 |
+
args = docopt(__doc__, version='0.0.0')
|
| 23 |
+
|
| 24 |
+
logger.setLevel(logging.INFO)
|
| 25 |
+
logger.addHandler(logging.StreamHandler())
|
| 26 |
+
|
| 27 |
+
license_modifiers = []
|
| 28 |
+
|
| 29 |
+
add_year = args['--add-year']
|
| 30 |
+
if add_year:
|
| 31 |
+
year = add_year.strip()
|
| 32 |
+
year = int(year)
|
| 33 |
+
def add_year(license):
|
| 34 |
+
years = license.copyright.years
|
| 35 |
+
years.add(year)
|
| 36 |
+
copyright = license.copyright.setYears(years)
|
| 37 |
+
return license.setCopyright(copyright)
|
| 38 |
+
license_modifiers.append(add_year)
|
| 39 |
+
|
| 40 |
+
set_author = args['--set-author']
|
| 41 |
+
if set_author:
|
| 42 |
+
author = set_author.strip()
|
| 43 |
+
from parsers import AUTHOR
|
| 44 |
+
author = AUTHOR.parseString(author)
|
| 45 |
+
def set_author(license):
|
| 46 |
+
copyright = license.copyright
|
| 47 |
+
copyright = copyright.setAuthors([author])
|
| 48 |
+
return license.setCopyright(copyright)
|
| 49 |
+
license_modifiers.append(set_author)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
filenames = args['<files>']
|
| 53 |
+
|
| 54 |
+
for path in filenames:
|
| 55 |
+
logger.info('filename: %s', path)
|
| 56 |
+
try:
|
| 57 |
+
process_file(path, license_modifiers)
|
| 58 |
+
except Exception, e:
|
| 59 |
+
logger.exception(e)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def process_file(path, license_modifiers):
|
| 63 |
+
import os
|
| 64 |
+
import tempfile
|
| 65 |
+
import shutil
|
| 66 |
+
from parsers import parse_file
|
| 67 |
+
|
| 68 |
+
license = parse_file(path)
|
| 69 |
+
for modifier in license_modifiers:
|
| 70 |
+
license = modifier(license)
|
| 71 |
+
|
| 72 |
+
fd, tmp_path = tempfile.mkstemp()
|
| 73 |
+
try:
|
| 74 |
+
with os.fdopen(fd, 'w') as f:
|
| 75 |
+
f.write(str(license))
|
| 76 |
+
except:
|
| 77 |
+
os.unlink(tmp_path)
|
| 78 |
+
raise
|
| 79 |
+
else:
|
| 80 |
+
shutil.move(tmp_path, path)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def stringify_years(years):
|
| 84 |
+
from parsers import Span
|
| 85 |
+
return ','.join(str(span) for span in Span.from_set(years))
|
tools/gpl/gpl/parsers.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from gpl.Pysec import Record
|
| 5 |
+
from gpl.Pysec import one_of
|
| 6 |
+
from gpl.Pysec import none_of
|
| 7 |
+
from gpl.Pysec import between
|
| 8 |
+
from gpl.Pysec import digits
|
| 9 |
+
from gpl.Pysec import match
|
| 10 |
+
from gpl.Pysec import sep_by
|
| 11 |
+
from gpl.Pysec import pair
|
| 12 |
+
from gpl.Pysec import Parser
|
| 13 |
+
from gpl.Pysec import until_one_of
|
| 14 |
+
from gpl.Pysec import option
|
| 15 |
+
from gpl.Pysec import space
|
| 16 |
+
from gpl.Pysec import spaces
|
| 17 |
+
from gpl.Pysec import quoted
|
| 18 |
+
#from gpl.Pysec import quoted_chars
|
| 19 |
+
from gpl.Pysec import char_range
|
| 20 |
+
from gpl.Pysec import many_chars
|
| 21 |
+
from gpl.Pysec import group_chars
|
| 22 |
+
from gpl.Pysec import skip_before
|
| 23 |
+
from gpl.Pysec import skip_after
|
| 24 |
+
from gpl.Pysec import skip_many
|
| 25 |
+
from gpl.Pysec import many
|
| 26 |
+
from gpl.Pysec import group
|
| 27 |
+
from gpl.Pysec import parser
|
| 28 |
+
from gpl.Pysec import ParseFailed
|
| 29 |
+
|
| 30 |
+
lift = Parser.lift
|
| 31 |
+
|
| 32 |
+
inline_space = one_of(" \v\f\t\r")
|
| 33 |
+
inline_spaces = skip_many(inline_space)
|
| 34 |
+
meaningful_spaces = many_chars(space)
|
| 35 |
+
|
| 36 |
+
def quoted_chars_inline(start, end):
|
| 37 |
+
return quoted(start, many_chars(none_of(end+'\n')), end)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def until_one_of_inline(chars):
|
| 41 |
+
return until_one_of(chars+'\n')
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def skip_tailspace_of_line(parser):
|
| 45 |
+
return skip_after(parser,
|
| 46 |
+
inline_spaces & option(None, match('\n')))
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@parser
|
| 50 |
+
def until_not_but(state0, should_not, but):
|
| 51 |
+
state = state0
|
| 52 |
+
values = []
|
| 53 |
+
while True:
|
| 54 |
+
try:
|
| 55 |
+
should_not_value, next_state = should_not(state)
|
| 56 |
+
return values, state
|
| 57 |
+
except ParseFailed:
|
| 58 |
+
value, state = but(state)
|
| 59 |
+
values.append(value)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def py_comment(parser):
|
| 63 |
+
return inline_spaces & match('#') & parser
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class Project(Record('name', 'description')):
|
| 67 |
+
|
| 68 |
+
@classmethod
|
| 69 |
+
def prepare(cls, name, description=None):
|
| 70 |
+
name = name.strip()
|
| 71 |
+
if description is not None:
|
| 72 |
+
description = description.strip()
|
| 73 |
+
return name, description
|
| 74 |
+
|
| 75 |
+
def __str__(self):
|
| 76 |
+
return self.name + (' : ' + self.description
|
| 77 |
+
if self.description
|
| 78 |
+
else '')
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
alphabet = char_range('a', 'z') + char_range('A', 'Z')
|
| 82 |
+
PROJECT_NAME = (group_chars([one_of(alphabet),
|
| 83 |
+
many_chars(one_of(alphabet + char_range('0', '9') + '-_'), 1)])
|
| 84 |
+
>> lift(str.strip))
|
| 85 |
+
PROJECT_NAME = skip_after(PROJECT_NAME, option(None, inline_spaces))
|
| 86 |
+
PROJECT_DESC = until_one_of('\n') >> lift(str.strip)
|
| 87 |
+
PROJECT_LINE = pair(PROJECT_NAME,
|
| 88 |
+
option(None, match(':') & PROJECT_DESC))
|
| 89 |
+
PROJECT_LINE = PROJECT_LINE >> lift(lambda seq: Project(*seq))
|
| 90 |
+
PROJECT_LINE = skip_before(inline_spaces, PROJECT_LINE)
|
| 91 |
+
PROJECT_LINE = skip_tailspace_of_line(PROJECT_LINE)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
COPYRIGHT_SIGN = match('Copyright (C)')
|
| 95 |
+
|
| 96 |
+
class Span(Record('start', 'end')):
|
| 97 |
+
|
| 98 |
+
@classmethod
|
| 99 |
+
def prepare(cls, start, end=None):
|
| 100 |
+
if end is None:
|
| 101 |
+
end = start
|
| 102 |
+
assert start <= end
|
| 103 |
+
return start, end
|
| 104 |
+
|
| 105 |
+
def as_set(self):
|
| 106 |
+
return set(range(self.start, self.end + 1))
|
| 107 |
+
|
| 108 |
+
@classmethod
|
| 109 |
+
def from_set(cls, valueset):
|
| 110 |
+
span = None
|
| 111 |
+
for value in sorted(valueset):
|
| 112 |
+
if span is None:
|
| 113 |
+
# at first
|
| 114 |
+
span = Span(value, value)
|
| 115 |
+
elif value == span.end + 1:
|
| 116 |
+
# continue current span
|
| 117 |
+
span = span.setEnd(value)
|
| 118 |
+
else:
|
| 119 |
+
# end current and start next span
|
| 120 |
+
yield span
|
| 121 |
+
span = Span(value, value)
|
| 122 |
+
if span is not None:
|
| 123 |
+
yield span
|
| 124 |
+
|
| 125 |
+
def __str__(self):
|
| 126 |
+
if self.start == self.end:
|
| 127 |
+
return str(self.start)
|
| 128 |
+
return '%d-%d' % self
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
YEAR = digits >> lift(int)
|
| 132 |
+
TAIL = option(None, match('-') & YEAR)
|
| 133 |
+
YEAR_SPAN = (pair(YEAR, TAIL) >> lift(lambda pair: Span(*pair)))
|
| 134 |
+
YEARS = (sep_by(YEAR_SPAN, match(','))
|
| 135 |
+
>> lift(lambda spans: reduce(set.union,
|
| 136 |
+
(span.as_set() for span in spans))))
|
| 137 |
+
|
| 138 |
+
class Author(Record('name', 'email')):
|
| 139 |
+
|
| 140 |
+
@classmethod
|
| 141 |
+
def prepare(self, name, email=None):
|
| 142 |
+
if not name and not email:
|
| 143 |
+
raise ValueError('either of name and email should not be empty')
|
| 144 |
+
return (name.strip() if name else None,
|
| 145 |
+
email.strip() if email else None)
|
| 146 |
+
|
| 147 |
+
def __str__(self):
|
| 148 |
+
name = self.name or ''
|
| 149 |
+
email = ('<' + self.email + '>') if self.email else ''
|
| 150 |
+
if not email:
|
| 151 |
+
return name
|
| 152 |
+
if not name:
|
| 153 |
+
return email
|
| 154 |
+
return name + ' ' + email
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
AUTHOR_NAME = until_one_of('<,\n') >> lift(str.strip)
|
| 158 |
+
AUTHOR_EMAIL = quoted_chars_inline('<', '>')
|
| 159 |
+
AUTHOR = (pair(option(None, AUTHOR_NAME), option(None, AUTHOR_EMAIL))
|
| 160 |
+
>> lift(lambda author: Author(*author)))
|
| 161 |
+
joiner = between(spaces, match(","), spaces)
|
| 162 |
+
AUTHORS = sep_by(AUTHOR, joiner)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class Copyright(Record('years', 'authors')):
|
| 166 |
+
def __str__(self):
|
| 167 |
+
years = ','.join(str(span) for span in Span.from_set(self.years))
|
| 168 |
+
authors = ', '.join(str(author) for author in self.authors)
|
| 169 |
+
return 'Copyright (C) %s %s' % (years, authors)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
COPYRIGHT_LINE = (COPYRIGHT_SIGN & inline_spaces &
|
| 173 |
+
pair(YEARS, inline_spaces & AUTHORS))
|
| 174 |
+
COPYRIGHT_LINE = skip_before(inline_spaces, COPYRIGHT_LINE)
|
| 175 |
+
COPYRIGHT_LINE = skip_tailspace_of_line(COPYRIGHT_LINE)
|
| 176 |
+
COPYRIGHT_LINE = COPYRIGHT_LINE >> lift(lambda seq: Copyright(*seq))
|
| 177 |
+
|
| 178 |
+
GENERIC_LINE = skip_after(many_chars(none_of('\n')), match('\n'))
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
class License(Record('prolog', 'project', 'copyright', 'epilog')):
|
| 182 |
+
|
| 183 |
+
def __str__(self):
|
| 184 |
+
return '\n'.join(self.prolog +
|
| 185 |
+
['# ' + str(self.project),
|
| 186 |
+
'# ' + str(self.copyright)] +
|
| 187 |
+
self.epilog + [''])
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
PROLOG = until_not_but(py_comment(PROJECT_LINE), GENERIC_LINE)
|
| 191 |
+
EPILOG = many(GENERIC_LINE)
|
| 192 |
+
LICENSE = (group([PROLOG,
|
| 193 |
+
py_comment(PROJECT_LINE),
|
| 194 |
+
py_comment(COPYRIGHT_LINE),
|
| 195 |
+
EPILOG])
|
| 196 |
+
>> lift(lambda seq: License(*seq)))
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def parse_file(path):
|
| 200 |
+
with file(path) as f:
|
| 201 |
+
text = f.read()
|
| 202 |
+
return LICENSE.parseString(text)
|
tools/gpl/gpl/tests/__init__.py
ADDED
|
File without changes
|
tools/gpl/gpl/tests/test_gpl.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from unittest import TestCase
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class SpanTest(TestCase):
|
| 6 |
+
|
| 7 |
+
def test_from_set(self):
|
| 8 |
+
from gpl.parsers import Span
|
| 9 |
+
self.assertEqual([Span(1)],
|
| 10 |
+
list(Span.from_set([1])))
|
| 11 |
+
self.assertEqual([Span(1, 2)],
|
| 12 |
+
list(Span.from_set([1, 2])))
|
| 13 |
+
self.assertEqual([Span(1, 2), Span(4)],
|
| 14 |
+
list(Span.from_set([1, 2, 4])))
|
| 15 |
+
self.assertEqual([Span(1, 2), Span(4, 6)],
|
| 16 |
+
list(Span.from_set([1, 2, 4, 5, 6])))
|
| 17 |
+
|
| 18 |
+
def test_str(self):
|
| 19 |
+
from gpl.parsers import Span
|
| 20 |
+
self.assertEqual('3', str(Span(3)))
|
| 21 |
+
self.assertEqual('3-4', str(Span(3, 4)))
|
| 22 |
+
self.assertEqual('3-6', str(Span(3, 6)))
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
project_line = ' pyhwp : hwp file format parser in python'
|
| 26 |
+
copyright_line = ' Copyright (C) 2010-2012 mete0r '
|
| 27 |
+
generic_line = ' abc '
|
| 28 |
+
LF = '\n'
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class ProjectTest(TestCase):
|
| 32 |
+
|
| 33 |
+
def test_project_name(self):
|
| 34 |
+
from gpl.parsers import PROJECT_NAME
|
| 35 |
+
self.assertEqual('pyhwp', PROJECT_NAME.parseString('pyhwp :'))
|
| 36 |
+
|
| 37 |
+
def test_project_desc(self):
|
| 38 |
+
from gpl.parsers import PROJECT_DESC
|
| 39 |
+
self.assertEqual('hwp file format parser in python',
|
| 40 |
+
PROJECT_DESC.parseString(' hwp file format parser in python '))
|
| 41 |
+
|
| 42 |
+
def test_project_line_with_lf(self):
|
| 43 |
+
from gpl.parsers import Project
|
| 44 |
+
from gpl.parsers import PROJECT_LINE
|
| 45 |
+
|
| 46 |
+
# ok with LF
|
| 47 |
+
self.assertEqual(Project('pyhwp', 'hwp file format parser in python'),
|
| 48 |
+
PROJECT_LINE.parseString(project_line + LF))
|
| 49 |
+
self.assertEqual(Project('pyhwp'),
|
| 50 |
+
PROJECT_LINE.parseString(' pyhwp ' + LF))
|
| 51 |
+
|
| 52 |
+
def test_project_line_without_lf(self):
|
| 53 |
+
from gpl.parsers import Project
|
| 54 |
+
from gpl.parsers import PROJECT_LINE
|
| 55 |
+
|
| 56 |
+
# ok without LF
|
| 57 |
+
self.assertEqual(Project('pyhwp', 'hwp file format parser in python'),
|
| 58 |
+
PROJECT_LINE.parseString(project_line))
|
| 59 |
+
self.assertEqual(Project('pyhwp'),
|
| 60 |
+
PROJECT_LINE.parseString(' pyhwp '))
|
| 61 |
+
|
| 62 |
+
def test_project_line_parser_doesnt_consume_after_lf(self):
|
| 63 |
+
from gpl.parsers import PROJECT_LINE
|
| 64 |
+
# make sure that the parser does not consume after LF
|
| 65 |
+
from gpl.Pysec import match
|
| 66 |
+
self.assertEqual(' NEXTLINE',
|
| 67 |
+
(PROJECT_LINE & match(' NEXTLINE')).parseString(project_line
|
| 68 |
+
+ LF + ' NEXTLINE'))
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class CopyrightTest(TestCase):
|
| 72 |
+
|
| 73 |
+
def test_stringify_years(self):
|
| 74 |
+
from gpl import stringify_years
|
| 75 |
+
self.assertEqual('2011-2012',
|
| 76 |
+
stringify_years([2011, 2012]))
|
| 77 |
+
self.assertEqual('2011-2013',
|
| 78 |
+
stringify_years([2011, 2012, 2013]))
|
| 79 |
+
self.assertEqual('2011-2013,2015',
|
| 80 |
+
stringify_years([2011, 2012, 2013, 2015]))
|
| 81 |
+
self.assertEqual('2009,2011-2013,2015',
|
| 82 |
+
stringify_years([2009, 2011, 2012, 2013, 2015]))
|
| 83 |
+
|
| 84 |
+
def test_copyright(self):
|
| 85 |
+
from gpl.parsers import COPYRIGHT_SIGN
|
| 86 |
+
self.assertTrue(COPYRIGHT_SIGN.parseString('Copyright (C)'))
|
| 87 |
+
|
| 88 |
+
from gpl.parsers import Span
|
| 89 |
+
self.assertEqual('2010', str(Span(2010)))
|
| 90 |
+
self.assertEqual('2010-2012', str(Span(2010, 2012)))
|
| 91 |
+
|
| 92 |
+
from gpl.parsers import YEAR_SPAN
|
| 93 |
+
self.assertEqual(Span(2010, 2012),
|
| 94 |
+
YEAR_SPAN.parseString('2010-2012'))
|
| 95 |
+
self.assertEqual(Span(2010, 2010),
|
| 96 |
+
YEAR_SPAN.parseString('2010'))
|
| 97 |
+
|
| 98 |
+
from gpl.parsers import YEARS
|
| 99 |
+
self.assertEqual(set([2010]),
|
| 100 |
+
YEARS.parseString('2010'))
|
| 101 |
+
self.assertEqual(set([2010, 2011]),
|
| 102 |
+
YEARS.parseString('2010,2011'))
|
| 103 |
+
self.assertEqual(set([2010, 2011, 2012]),
|
| 104 |
+
YEARS.parseString('2010-2012'))
|
| 105 |
+
self.assertEqual(set([2010, 2011, 2013, 2014, 2015, 2017]),
|
| 106 |
+
YEARS.parseString('2010,2011,2013-2015,2017'))
|
| 107 |
+
|
| 108 |
+
from gpl.parsers import AUTHOR_NAME
|
| 109 |
+
self.assertEqual('Hello World',
|
| 110 |
+
AUTHOR_NAME.parseString('Hello World'))
|
| 111 |
+
self.assertEqual('Hello World',
|
| 112 |
+
AUTHOR_NAME.parseString('Hello World <'))
|
| 113 |
+
|
| 114 |
+
from gpl.parsers import AUTHOR_EMAIL
|
| 115 |
+
self.assertEqual('user@example.tld',
|
| 116 |
+
AUTHOR_EMAIL.parseString('<user@example.tld>'))
|
| 117 |
+
|
| 118 |
+
from gpl.parsers import Author
|
| 119 |
+
from gpl.parsers import AUTHOR
|
| 120 |
+
self.assertEqual(Author('hong gil-dong', 'hongd@example.tld'),
|
| 121 |
+
AUTHOR.parseString('hong gil-dong <hongd@example.tld>'))
|
| 122 |
+
self.assertEqual(Author('hong gil-dong'),
|
| 123 |
+
(AUTHOR.parseString('hong gil-dong')))
|
| 124 |
+
self.assertEqual(Author(None, 'hongd@example.tld'),
|
| 125 |
+
(AUTHOR.parseString('<hongd@example.tld>')))
|
| 126 |
+
|
| 127 |
+
from gpl.parsers import AUTHORS
|
| 128 |
+
self.assertEqual([Author('mete0r'),
|
| 129 |
+
Author('hong gil-dong', 'hongd@ex.tld')],
|
| 130 |
+
AUTHORS.parseString('mete0r, hong gil-dong <hongd@ex.tld>'))
|
| 131 |
+
|
| 132 |
+
from gpl.parsers import Copyright
|
| 133 |
+
from gpl.parsers import COPYRIGHT_LINE
|
| 134 |
+
# ok with LF
|
| 135 |
+
self.assertEqual(Copyright(set([2010, 2011, 2012]),
|
| 136 |
+
[Author('mete0r')]),
|
| 137 |
+
(COPYRIGHT_LINE.parseString(copyright_line + LF)))
|
| 138 |
+
|
| 139 |
+
# ok without LF
|
| 140 |
+
self.assertEqual(Copyright(set([2010, 2011, 2012]),
|
| 141 |
+
[Author('mete0r')]),
|
| 142 |
+
(COPYRIGHT_LINE.parseString(copyright_line)))
|
| 143 |
+
|
| 144 |
+
# make sure that the parser does not consume after the LF
|
| 145 |
+
from gpl.Pysec import match
|
| 146 |
+
self.assertEqual(' NEXTLINE',
|
| 147 |
+
(COPYRIGHT_LINE & match(' NEXTLINE')).parseString(copyright_line + LF + ' NEXTLINE'))
|
| 148 |
+
|
| 149 |
+
def test_generic_line(self):
|
| 150 |
+
from gpl.parsers import GENERIC_LINE
|
| 151 |
+
self.assertEqual(generic_line,
|
| 152 |
+
GENERIC_LINE.parseString(generic_line + LF))
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
class LicenseTest(TestCase):
|
| 156 |
+
def test_license(self):
|
| 157 |
+
from gpl.parsers import LICENSE
|
| 158 |
+
|
| 159 |
+
text = '''#!/usr/bin/python
|
| 160 |
+
# -*- coding: utf-8 -*-
|
| 161 |
+
#
|
| 162 |
+
# ''' + project_line + '''
|
| 163 |
+
# ''' + copyright_line + '''
|
| 164 |
+
#
|
| 165 |
+
# This file is part of pyhwp project.
|
| 166 |
+
#
|
| 167 |
+
# license text.
|
| 168 |
+
|
| 169 |
+
import unittest
|
| 170 |
+
'''
|
| 171 |
+
print LICENSE.parseString(text)
|
tools/gpl/setup.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from setuptools import setup, find_packages
|
| 2 |
+
setup(name='gpl',
|
| 3 |
+
packages=find_packages(),
|
| 4 |
+
install_requires=['docopt'],
|
| 5 |
+
entry_points={
|
| 6 |
+
'console_scripts': [
|
| 7 |
+
'gpl = gpl:main'
|
| 8 |
+
]
|
| 9 |
+
})
|
tools/jingodf/jingodf/__init__.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#! -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
class Jing(object):
|
| 4 |
+
|
| 5 |
+
def __init__(self, executable='jing'):
|
| 6 |
+
''' External Jing executable
|
| 7 |
+
|
| 8 |
+
:param executable: path to jing executable. Default: jing
|
| 9 |
+
'''
|
| 10 |
+
self.executable = executable
|
| 11 |
+
|
| 12 |
+
def __call__(self, *args, **kwargs):
|
| 13 |
+
args = list(args)
|
| 14 |
+
|
| 15 |
+
import subprocess
|
| 16 |
+
if isinstance(self.executable, basestring):
|
| 17 |
+
args[0:0] = [self.executable]
|
| 18 |
+
else:
|
| 19 |
+
args[0:0] = self.executable
|
| 20 |
+
|
| 21 |
+
p = subprocess.Popen(args, stdout=subprocess.PIPE, **kwargs)
|
| 22 |
+
for line in p.stdout:
|
| 23 |
+
filename, line, column, level, msg = line.split(':', 4)
|
| 24 |
+
level = level.strip()
|
| 25 |
+
msg = msg.strip()
|
| 26 |
+
yield dict(filename=filename,
|
| 27 |
+
line=line, column=column,
|
| 28 |
+
level=level, msg=msg)
|
| 29 |
+
p.wait()
|
| 30 |
+
yield p.returncode
|
| 31 |
+
|
| 32 |
+
def validate(self, rngfile, xmlfile):
|
| 33 |
+
return self('-i', rngfile, xmlfile)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class XmlLint(object):
|
| 37 |
+
def __init__(self, executable='xmllint'):
|
| 38 |
+
''' External xmllint executable
|
| 39 |
+
|
| 40 |
+
:param executable: path to xmllint executable. Default: xmllint
|
| 41 |
+
'''
|
| 42 |
+
self.executable = executable
|
| 43 |
+
|
| 44 |
+
def __call__(self, *args, **kwargs):
|
| 45 |
+
args = list(args)
|
| 46 |
+
|
| 47 |
+
import subprocess
|
| 48 |
+
if isinstance(self.executable, basestring):
|
| 49 |
+
args[0:0] = [self.executable]
|
| 50 |
+
else:
|
| 51 |
+
args[0:0] = self.executable
|
| 52 |
+
|
| 53 |
+
p = subprocess.Popen(args, stderr=subprocess.PIPE, **kwargs)
|
| 54 |
+
import re
|
| 55 |
+
regex = re.compile('(.*)Relax-NG validity error : (.*)')
|
| 56 |
+
for line in p.stderr:
|
| 57 |
+
if line.endswith(' validates\n'):
|
| 58 |
+
continue
|
| 59 |
+
elif line.endswith(' fails to validate\n'):
|
| 60 |
+
continue
|
| 61 |
+
line = line.strip()
|
| 62 |
+
m = regex.match(line)
|
| 63 |
+
if m:
|
| 64 |
+
location = m.group(1)
|
| 65 |
+
msg = m.group(2)
|
| 66 |
+
if location:
|
| 67 |
+
filename, line_no, element, _ = line.split(':', 3)
|
| 68 |
+
element = element.strip()
|
| 69 |
+
level = 'error'
|
| 70 |
+
msg = msg.strip()
|
| 71 |
+
yield dict(filename=filename,
|
| 72 |
+
line=line_no, column=element,
|
| 73 |
+
level=level, msg=msg)
|
| 74 |
+
else:
|
| 75 |
+
print '*', line
|
| 76 |
+
p.wait()
|
| 77 |
+
yield p.returncode
|
| 78 |
+
|
| 79 |
+
def validate(self, rngfile, xmlfile):
|
| 80 |
+
return self('--noout', '--relaxng', rngfile, xmlfile)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class ODFValidator(object):
|
| 84 |
+
def __init__(self, engine):
|
| 85 |
+
self.engine = engine
|
| 86 |
+
|
| 87 |
+
def validate_manifest_xml(self, version, xmlfile):
|
| 88 |
+
rng_files = {
|
| 89 |
+
'1.0': 'OpenDocument-manifest-schema-v1.0-os.rng',
|
| 90 |
+
'1.1': 'OpenDocument-manifest-schema-v1.1.rng',
|
| 91 |
+
'1.2': 'OpenDocument-v1.2-os-manifest-schema.rng',
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
import pkg_resources
|
| 95 |
+
rng_filename = rng_files[version]
|
| 96 |
+
rng_file = pkg_resources.resource_filename('jingodf',
|
| 97 |
+
'schema/'+rng_filename)
|
| 98 |
+
return self.engine.validate(rng_file, xmlfile)
|
| 99 |
+
|
| 100 |
+
def validate_opendocument_xml(self, version, xmlfile):
|
| 101 |
+
rng_files = {
|
| 102 |
+
'1.0': 'OpenDocument-schema-v1.0-os.rng',
|
| 103 |
+
'1.1': 'OpenDocument-schema-v1.1.rng',
|
| 104 |
+
'1.2': 'OpenDocument-v1.2-os-schema.rng',
|
| 105 |
+
}
|
| 106 |
+
import pkg_resources
|
| 107 |
+
rng_filename = rng_files[version]
|
| 108 |
+
rng_file = pkg_resources.resource_filename('jingodf',
|
| 109 |
+
'schema/'+rng_filename)
|
| 110 |
+
return self.engine.validate(rng_file, xmlfile)
|
| 111 |
+
|
| 112 |
+
def validate_odf(self, version, odffile):
|
| 113 |
+
|
| 114 |
+
import os.path
|
| 115 |
+
from zipfile import ZipFile
|
| 116 |
+
zipfile = ZipFile(odffile, 'r')
|
| 117 |
+
try:
|
| 118 |
+
import tempfile
|
| 119 |
+
tmpdir = tempfile.mkdtemp()
|
| 120 |
+
try:
|
| 121 |
+
path = 'META-INF/manifest.xml'
|
| 122 |
+
zipfile.extract(path, tmpdir)
|
| 123 |
+
results = self.validate_manifest_xml(version,
|
| 124 |
+
os.path.join(tmpdir, path))
|
| 125 |
+
for result in results:
|
| 126 |
+
if isinstance(result, dict):
|
| 127 |
+
result['filename'] = path
|
| 128 |
+
yield result
|
| 129 |
+
|
| 130 |
+
path = 'styles.xml'
|
| 131 |
+
zipfile.extract(path, tmpdir)
|
| 132 |
+
results = self.validate_opendocument_xml(version,
|
| 133 |
+
os.path.join(tmpdir, path))
|
| 134 |
+
for result in results:
|
| 135 |
+
if isinstance(result, dict):
|
| 136 |
+
result['filename'] = path
|
| 137 |
+
yield result
|
| 138 |
+
|
| 139 |
+
path = 'content.xml'
|
| 140 |
+
zipfile.extract(path, tmpdir)
|
| 141 |
+
results = self.validate_opendocument_xml(version,
|
| 142 |
+
os.path.join(tmpdir, path))
|
| 143 |
+
for result in results:
|
| 144 |
+
if isinstance(result, dict):
|
| 145 |
+
result['filename'] = path
|
| 146 |
+
yield result
|
| 147 |
+
|
| 148 |
+
finally:
|
| 149 |
+
import shutil
|
| 150 |
+
shutil.rmtree(tmpdir)
|
| 151 |
+
finally:
|
| 152 |
+
zipfile.close()
|
| 153 |
+
|
| 154 |
+
from opster import command
|
| 155 |
+
|
| 156 |
+
@command()
|
| 157 |
+
def validate(odf_file,
|
| 158 |
+
odf_version=('', '1.2', 'OpenDocument specification version'),
|
| 159 |
+
engine=('', 'jing', 'Relax-NG Validator engine')):
|
| 160 |
+
''' Validate an ODF file against the OpenDocument Relax-NG Schema. '''
|
| 161 |
+
|
| 162 |
+
engines = dict(jing=Jing, xmllint=XmlLint)
|
| 163 |
+
engine_class = engines.get(engine, Jing)
|
| 164 |
+
engine = engine_class()
|
| 165 |
+
odf_validator = ODFValidator(engine)
|
| 166 |
+
results = odf_validator.validate_odf(odf_version, odf_file)
|
| 167 |
+
|
| 168 |
+
def print_result(result):
|
| 169 |
+
if isinstance(result, dict):
|
| 170 |
+
print '%(filename)s:%(line)s:%(column)s: %(level)s: %(msg)s'%result
|
| 171 |
+
|
| 172 |
+
for result in results:
|
| 173 |
+
print_result(result)
|
| 174 |
+
|
| 175 |
+
def main():
|
| 176 |
+
validate.command()
|
tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.0-os.rng
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
OASIS OpenDocument v1.0
|
| 4 |
+
OASIS standard, 1 May 2005
|
| 5 |
+
Relax-NG Manifest Schema
|
| 6 |
+
|
| 7 |
+
$Id$
|
| 8 |
+
|
| 9 |
+
© 2002-2005 OASIS Open
|
| 10 |
+
© 1999-2005 Sun Microsystems, Inc.
|
| 11 |
+
-->
|
| 12 |
+
|
| 13 |
+
<grammar
|
| 14 |
+
xmlns="http://relaxng.org/ns/structure/1.0"
|
| 15 |
+
|
| 16 |
+
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
|
| 17 |
+
|
| 18 |
+
xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
|
| 19 |
+
<define name="manifest">
|
| 20 |
+
<element name="manifest:manifest">
|
| 21 |
+
<oneOrMore>
|
| 22 |
+
<ref name="file-entry"/>
|
| 23 |
+
</oneOrMore>
|
| 24 |
+
</element>
|
| 25 |
+
</define>
|
| 26 |
+
|
| 27 |
+
<start>
|
| 28 |
+
<choice>
|
| 29 |
+
<ref name="manifest"/>
|
| 30 |
+
</choice>
|
| 31 |
+
</start>
|
| 32 |
+
<define name="file-entry">
|
| 33 |
+
<element name="manifest:file-entry">
|
| 34 |
+
<ref name="file-entry-attlist"/>
|
| 35 |
+
<optional>
|
| 36 |
+
<ref name="encryption-data"/>
|
| 37 |
+
</optional>
|
| 38 |
+
</element>
|
| 39 |
+
</define>
|
| 40 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 41 |
+
<attribute name="manifest:full-path">
|
| 42 |
+
<data type="string"/>
|
| 43 |
+
</attribute>
|
| 44 |
+
</define>
|
| 45 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 46 |
+
<optional>
|
| 47 |
+
<attribute name="manifest:size">
|
| 48 |
+
<data type="nonNegativeInteger"/>
|
| 49 |
+
</attribute>
|
| 50 |
+
</optional>
|
| 51 |
+
</define>
|
| 52 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 53 |
+
<attribute name="manifest:media-type">
|
| 54 |
+
<data type="string"/>
|
| 55 |
+
</attribute>
|
| 56 |
+
</define>
|
| 57 |
+
<define name="encryption-data">
|
| 58 |
+
<element name="manifest:encryption-data">
|
| 59 |
+
<ref name="encryption-data-attlist"/>
|
| 60 |
+
<ref name="algorithm"/>
|
| 61 |
+
<ref name="key-derivation"/>
|
| 62 |
+
</element>
|
| 63 |
+
</define>
|
| 64 |
+
<define name="encryption-data-attlist" combine="interleave">
|
| 65 |
+
<attribute name="manifest:checksum-type">
|
| 66 |
+
<data type="string"/>
|
| 67 |
+
</attribute>
|
| 68 |
+
</define>
|
| 69 |
+
<define name="encryption-data-attlist" combine="interleave">
|
| 70 |
+
<attribute name="manifest:checksum">
|
| 71 |
+
<data type="base64Binary"/>
|
| 72 |
+
</attribute>
|
| 73 |
+
</define>
|
| 74 |
+
<define name="algorithm">
|
| 75 |
+
<element name="manifest:algorithm">
|
| 76 |
+
<ref name="algorithm-attlist"/>
|
| 77 |
+
<empty/>
|
| 78 |
+
</element>
|
| 79 |
+
</define>
|
| 80 |
+
<define name="algorithm-attlist" combine="interleave">
|
| 81 |
+
<attribute name="manifest:algorithm-name">
|
| 82 |
+
<data type="string"/>
|
| 83 |
+
</attribute>
|
| 84 |
+
</define>
|
| 85 |
+
<define name="algorithm-attlist" combine="interleave">
|
| 86 |
+
<attribute name="manifest:initialisation-vector">
|
| 87 |
+
<data type="base64Binary"/>
|
| 88 |
+
</attribute>
|
| 89 |
+
</define>
|
| 90 |
+
<define name="key-derivation">
|
| 91 |
+
<element name="manifest:key-derivation">
|
| 92 |
+
<ref name="key-derivation-attlist"/>
|
| 93 |
+
<empty/>
|
| 94 |
+
</element>
|
| 95 |
+
</define>
|
| 96 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 97 |
+
<attribute name="manifest:key-derivation-name">
|
| 98 |
+
<data type="string"/>
|
| 99 |
+
</attribute>
|
| 100 |
+
</define>
|
| 101 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 102 |
+
<attribute name="manifest:salt">
|
| 103 |
+
<data type="base64Binary"/>
|
| 104 |
+
</attribute>
|
| 105 |
+
</define>
|
| 106 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 107 |
+
<attribute name="manifest:iteration-count">
|
| 108 |
+
<data type="nonNegativeInteger"/>
|
| 109 |
+
</attribute>
|
| 110 |
+
</define>
|
| 111 |
+
</grammar>
|
tools/jingodf/jingodf/schema/OpenDocument-manifest-schema-v1.1.rng
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
OASIS OpenDocument v1.1
|
| 4 |
+
OASIS Standard, 1 Feb 2007
|
| 5 |
+
Relax-NG Manifest Schema
|
| 6 |
+
|
| 7 |
+
$Id$
|
| 8 |
+
|
| 9 |
+
© 2002-2007 OASIS Open
|
| 10 |
+
© 1999-2007 Sun Microsystems, Inc.
|
| 11 |
+
-->
|
| 12 |
+
|
| 13 |
+
<grammar
|
| 14 |
+
xmlns="http://relaxng.org/ns/structure/1.0"
|
| 15 |
+
|
| 16 |
+
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
|
| 17 |
+
|
| 18 |
+
xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
|
| 19 |
+
<define name="manifest">
|
| 20 |
+
<element name="manifest:manifest">
|
| 21 |
+
<oneOrMore>
|
| 22 |
+
<ref name="file-entry"/>
|
| 23 |
+
</oneOrMore>
|
| 24 |
+
</element>
|
| 25 |
+
</define>
|
| 26 |
+
|
| 27 |
+
<start>
|
| 28 |
+
<choice>
|
| 29 |
+
<ref name="manifest"/>
|
| 30 |
+
</choice>
|
| 31 |
+
</start>
|
| 32 |
+
<define name="file-entry">
|
| 33 |
+
<element name="manifest:file-entry">
|
| 34 |
+
<ref name="file-entry-attlist"/>
|
| 35 |
+
<optional>
|
| 36 |
+
<ref name="encryption-data"/>
|
| 37 |
+
</optional>
|
| 38 |
+
</element>
|
| 39 |
+
</define>
|
| 40 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 41 |
+
<attribute name="manifest:full-path">
|
| 42 |
+
<data type="string"/>
|
| 43 |
+
</attribute>
|
| 44 |
+
</define>
|
| 45 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 46 |
+
<optional>
|
| 47 |
+
<attribute name="manifest:size">
|
| 48 |
+
<data type="nonNegativeInteger"/>
|
| 49 |
+
</attribute>
|
| 50 |
+
</optional>
|
| 51 |
+
</define>
|
| 52 |
+
<define name="file-entry-attlist" combine="interleave">
|
| 53 |
+
<attribute name="manifest:media-type">
|
| 54 |
+
<data type="string"/>
|
| 55 |
+
</attribute>
|
| 56 |
+
</define>
|
| 57 |
+
<define name="encryption-data">
|
| 58 |
+
<element name="manifest:encryption-data">
|
| 59 |
+
<ref name="encryption-data-attlist"/>
|
| 60 |
+
<ref name="algorithm"/>
|
| 61 |
+
<ref name="key-derivation"/>
|
| 62 |
+
</element>
|
| 63 |
+
</define>
|
| 64 |
+
<define name="encryption-data-attlist" combine="interleave">
|
| 65 |
+
<attribute name="manifest:checksum-type">
|
| 66 |
+
<data type="string"/>
|
| 67 |
+
</attribute>
|
| 68 |
+
</define>
|
| 69 |
+
<define name="encryption-data-attlist" combine="interleave">
|
| 70 |
+
<attribute name="manifest:checksum">
|
| 71 |
+
<data type="base64Binary"/>
|
| 72 |
+
</attribute>
|
| 73 |
+
</define>
|
| 74 |
+
<define name="algorithm">
|
| 75 |
+
<element name="manifest:algorithm">
|
| 76 |
+
<ref name="algorithm-attlist"/>
|
| 77 |
+
<empty/>
|
| 78 |
+
</element>
|
| 79 |
+
</define>
|
| 80 |
+
<define name="algorithm-attlist" combine="interleave">
|
| 81 |
+
<attribute name="manifest:algorithm-name">
|
| 82 |
+
<data type="string"/>
|
| 83 |
+
</attribute>
|
| 84 |
+
</define>
|
| 85 |
+
<define name="algorithm-attlist" combine="interleave">
|
| 86 |
+
<attribute name="manifest:initialisation-vector">
|
| 87 |
+
<data type="base64Binary"/>
|
| 88 |
+
</attribute>
|
| 89 |
+
</define>
|
| 90 |
+
<define name="key-derivation">
|
| 91 |
+
<element name="manifest:key-derivation">
|
| 92 |
+
<ref name="key-derivation-attlist"/>
|
| 93 |
+
<empty/>
|
| 94 |
+
</element>
|
| 95 |
+
</define>
|
| 96 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 97 |
+
<attribute name="manifest:key-derivation-name">
|
| 98 |
+
<data type="string"/>
|
| 99 |
+
</attribute>
|
| 100 |
+
</define>
|
| 101 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 102 |
+
<attribute name="manifest:salt">
|
| 103 |
+
<data type="base64Binary"/>
|
| 104 |
+
</attribute>
|
| 105 |
+
</define>
|
| 106 |
+
<define name="key-derivation-attlist" combine="interleave">
|
| 107 |
+
<attribute name="manifest:iteration-count">
|
| 108 |
+
<data type="nonNegativeInteger"/>
|
| 109 |
+
</attribute>
|
| 110 |
+
</define>
|
| 111 |
+
</grammar>
|
tools/jingodf/jingodf/schema/OpenDocument-schema-v1.0-os.rng
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tools/jingodf/jingodf/schema/OpenDocument-schema-v1.1.rng
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tools/jingodf/jingodf/schema/OpenDocument-strict-schema-v1.1.rng
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
OASIS OpenDocument v1.1
|
| 4 |
+
OASIS Standard, 1 Feb 2007
|
| 5 |
+
Strict Relax-NG Schema
|
| 6 |
+
|
| 7 |
+
$Id$
|
| 8 |
+
|
| 9 |
+
© 2002-2007 OASIS Open
|
| 10 |
+
© 1999-2007 Sun Microsystems, Inc.
|
| 11 |
+
-->
|
| 12 |
+
|
| 13 |
+
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
|
| 14 |
+
<include href="OpenDocument-schema-v1.1.rng">
|
| 15 |
+
<define name="office-meta-content">
|
| 16 |
+
<ref name="office-meta-content-strict"/>
|
| 17 |
+
</define>
|
| 18 |
+
<define name="style-page-layout-properties-content">
|
| 19 |
+
<ref name="style-page-layout-properties-content-strict"/>
|
| 20 |
+
</define>
|
| 21 |
+
<define name="style-header-footer-properties-content">
|
| 22 |
+
<ref name="style-header-footer-properties-content-strict"/>
|
| 23 |
+
</define>
|
| 24 |
+
<define name="style-drawing-page-properties-content">
|
| 25 |
+
<ref name="style-drawing-page-properties-content-strict"/>
|
| 26 |
+
</define>
|
| 27 |
+
<define name="style-text-properties-content">
|
| 28 |
+
<ref name="style-text-properties-content-strict"/>
|
| 29 |
+
</define>
|
| 30 |
+
<define name="style-paragraph-properties-content">
|
| 31 |
+
<ref name="style-paragraph-properties-content-strict"/>
|
| 32 |
+
</define>
|
| 33 |
+
<define name="style-ruby-properties-content">
|
| 34 |
+
<ref name="style-ruby-properties-content-strict"/>
|
| 35 |
+
</define>
|
| 36 |
+
<define name="style-section-properties-content">
|
| 37 |
+
<ref name="style-section-properties-content-strict"/>
|
| 38 |
+
</define>
|
| 39 |
+
<define name="style-list-level-properties-content">
|
| 40 |
+
<ref name="style-list-level-properties-content-strict"/>
|
| 41 |
+
</define>
|
| 42 |
+
<define name="style-table-properties-content">
|
| 43 |
+
<ref name="style-table-properties-content-strict"/>
|
| 44 |
+
</define>
|
| 45 |
+
<define name="style-table-column-properties-content">
|
| 46 |
+
<ref name="style-table-column-properties-content-strict"/>
|
| 47 |
+
</define>
|
| 48 |
+
<define name="style-table-row-properties-content">
|
| 49 |
+
<ref name="style-table-row-properties-content-strict"/>
|
| 50 |
+
</define>
|
| 51 |
+
<define name="style-table-cell-properties-content">
|
| 52 |
+
<ref name="style-table-cell-properties-content-strict"/>
|
| 53 |
+
</define>
|
| 54 |
+
<define name="style-graphic-properties-content">
|
| 55 |
+
<ref name="style-graphic-properties-content-strict"/>
|
| 56 |
+
</define>
|
| 57 |
+
<define name="style-chart-properties-content">
|
| 58 |
+
<ref name="style-chart-properties-content-strict"/>
|
| 59 |
+
</define>
|
| 60 |
+
</include>
|
| 61 |
+
</grammar>
|
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-dsig-schema.rng
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
Open Document Format for Office Applications (OpenDocument) Version 1.2
|
| 4 |
+
OASIS Standard, 29 September 2011
|
| 5 |
+
Digital Signatures Relax-NG Schema
|
| 6 |
+
Source: http://docs.oasis-open.org/office/v1.2/os/
|
| 7 |
+
Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
|
| 8 |
+
|
| 9 |
+
All capitalized terms in the following text have the meanings assigned to them
|
| 10 |
+
in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
|
| 11 |
+
full Policy may be found at the OASIS website.
|
| 12 |
+
|
| 13 |
+
This document and translations of it may be copied and furnished to others, and
|
| 14 |
+
derivative works that comment on or otherwise explain it or assist in its
|
| 15 |
+
implementation may be prepared, copied, published, and distributed, in whole or
|
| 16 |
+
in part, without restriction of any kind, provided that the above copyright
|
| 17 |
+
notice and this section are included on all such copies and derivative works.
|
| 18 |
+
However, this document itself may not be modified in any way, including by
|
| 19 |
+
removing the copyright notice or references to OASIS, except as needed for the
|
| 20 |
+
purpose of developing any document or deliverable produced by an OASIS
|
| 21 |
+
Technical Committee (in which case the rules applicable to copyrights, as set
|
| 22 |
+
forth in the OASIS IPR Policy, must be followed) or as required to translate it
|
| 23 |
+
into languages other than English.
|
| 24 |
+
|
| 25 |
+
The limited permissions granted above are perpetual and will not be revoked by
|
| 26 |
+
OASIS or its successors or assigns.
|
| 27 |
+
|
| 28 |
+
This document and the information contained herein is provided on an "AS IS"
|
| 29 |
+
basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
| 30 |
+
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
|
| 31 |
+
INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
|
| 32 |
+
FITNESS FOR A PARTICULAR PURPOSE.
|
| 33 |
+
-->
|
| 34 |
+
|
| 35 |
+
<grammar
|
| 36 |
+
xmlns="http://relaxng.org/ns/structure/1.0"
|
| 37 |
+
|
| 38 |
+
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
|
| 39 |
+
|
| 40 |
+
xmlns:dsig="urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0"
|
| 41 |
+
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
| 42 |
+
>
|
| 43 |
+
<start>
|
| 44 |
+
<choice>
|
| 45 |
+
<ref name="dsig-document-signatures"/>
|
| 46 |
+
</choice>
|
| 47 |
+
</start>
|
| 48 |
+
<define name="dsig-document-signatures">
|
| 49 |
+
<element name="dsig:document-signatures">
|
| 50 |
+
<ref name="dsig-document-signatures-attlist"/>
|
| 51 |
+
<oneOrMore>
|
| 52 |
+
<ref name="ds-signature"/>
|
| 53 |
+
</oneOrMore>
|
| 54 |
+
</element>
|
| 55 |
+
</define>
|
| 56 |
+
<define name="dsig-document-signatures-attlist">
|
| 57 |
+
<attribute name="dsig:version">
|
| 58 |
+
<value>1.2</value>
|
| 59 |
+
</attribute>
|
| 60 |
+
</define>
|
| 61 |
+
<define name="ds-signature">
|
| 62 |
+
<element name="ds:Signature">
|
| 63 |
+
<!-- The permitted content of this element is the permitted -->
|
| 64 |
+
<!-- content of the Signature element defined by W3C XML -->
|
| 65 |
+
<!-- Signature Syntax and Processing (Second Edition). -->
|
| 66 |
+
<!-- See OpenDocument v1.2 part 3, section 4.3. -->
|
| 67 |
+
<ref name="dsMarkup"/>
|
| 68 |
+
</element>
|
| 69 |
+
</define>
|
| 70 |
+
<define name="dsMarkup">
|
| 71 |
+
<zeroOrMore>
|
| 72 |
+
<choice>
|
| 73 |
+
<attribute>
|
| 74 |
+
<anyName/>
|
| 75 |
+
</attribute>
|
| 76 |
+
<text/>
|
| 77 |
+
<element>
|
| 78 |
+
<anyName/>
|
| 79 |
+
<ref name="dsMarkup"/>
|
| 80 |
+
</element>
|
| 81 |
+
</choice>
|
| 82 |
+
</zeroOrMore>
|
| 83 |
+
</define>
|
| 84 |
+
</grammar>
|
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-manifest-schema.rng
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
Open Document Format for Office Applications (OpenDocument) Version 1.2
|
| 4 |
+
OASIS Standard, 29 September 2011
|
| 5 |
+
Manifest Relax-NG Schema
|
| 6 |
+
Source: http://docs.oasis-open.org/office/v1.2/os/
|
| 7 |
+
Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
|
| 8 |
+
|
| 9 |
+
All capitalized terms in the following text have the meanings assigned to them
|
| 10 |
+
in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
|
| 11 |
+
full Policy may be found at the OASIS website.
|
| 12 |
+
|
| 13 |
+
This document and translations of it may be copied and furnished to others, and
|
| 14 |
+
derivative works that comment on or otherwise explain it or assist in its
|
| 15 |
+
implementation may be prepared, copied, published, and distributed, in whole or
|
| 16 |
+
in part, without restriction of any kind, provided that the above copyright
|
| 17 |
+
notice and this section are included on all such copies and derivative works.
|
| 18 |
+
However, this document itself may not be modified in any way, including by
|
| 19 |
+
removing the copyright notice or references to OASIS, except as needed for the
|
| 20 |
+
purpose of developing any document or deliverable produced by an OASIS
|
| 21 |
+
Technical Committee (in which case the rules applicable to copyrights, as set
|
| 22 |
+
forth in the OASIS IPR Policy, must be followed) or as required to translate it
|
| 23 |
+
into languages other than English.
|
| 24 |
+
|
| 25 |
+
The limited permissions granted above are perpetual and will not be revoked by
|
| 26 |
+
OASIS or its successors or assigns.
|
| 27 |
+
|
| 28 |
+
This document and the information contained herein is provided on an "AS IS"
|
| 29 |
+
basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
| 30 |
+
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
|
| 31 |
+
INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
|
| 32 |
+
FITNESS FOR A PARTICULAR PURPOSE.
|
| 33 |
+
-->
|
| 34 |
+
<grammar
|
| 35 |
+
xmlns="http://relaxng.org/ns/structure/1.0"
|
| 36 |
+
|
| 37 |
+
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
|
| 38 |
+
|
| 39 |
+
xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
| 40 |
+
>
|
| 41 |
+
<start>
|
| 42 |
+
<choice>
|
| 43 |
+
<ref name="manifest"/>
|
| 44 |
+
</choice>
|
| 45 |
+
</start>
|
| 46 |
+
<define name="manifest">
|
| 47 |
+
<element name="manifest:manifest">
|
| 48 |
+
<ref name="manifest-attlist"/>
|
| 49 |
+
<oneOrMore>
|
| 50 |
+
<ref name="file-entry"/>
|
| 51 |
+
</oneOrMore>
|
| 52 |
+
</element>
|
| 53 |
+
</define>
|
| 54 |
+
<define name="manifest-attlist">
|
| 55 |
+
<attribute name="manifest:version">
|
| 56 |
+
<value>1.2</value>
|
| 57 |
+
</attribute>
|
| 58 |
+
</define>
|
| 59 |
+
<define name="file-entry">
|
| 60 |
+
<element name="manifest:file-entry">
|
| 61 |
+
<ref name="file-entry-attlist"/>
|
| 62 |
+
<optional>
|
| 63 |
+
<ref name="encryption-data"/>
|
| 64 |
+
</optional>
|
| 65 |
+
</element>
|
| 66 |
+
</define>
|
| 67 |
+
<define name="file-entry-attlist">
|
| 68 |
+
<interleave>
|
| 69 |
+
<attribute name="manifest:full-path">
|
| 70 |
+
<ref name="string"/>
|
| 71 |
+
</attribute>
|
| 72 |
+
<optional>
|
| 73 |
+
<attribute name="manifest:size">
|
| 74 |
+
<ref name="nonNegativeInteger"/>
|
| 75 |
+
</attribute>
|
| 76 |
+
</optional>
|
| 77 |
+
<attribute name="manifest:media-type">
|
| 78 |
+
<ref name="string"/>
|
| 79 |
+
</attribute>
|
| 80 |
+
<optional>
|
| 81 |
+
<attribute name="manifest:preferred-view-mode">
|
| 82 |
+
<choice>
|
| 83 |
+
<value>edit</value>
|
| 84 |
+
<value>presentation-slide-show</value>
|
| 85 |
+
<value>read-only</value>
|
| 86 |
+
<ref name="namespacedToken"/>
|
| 87 |
+
</choice>
|
| 88 |
+
</attribute>
|
| 89 |
+
</optional>
|
| 90 |
+
<optional>
|
| 91 |
+
<attribute name="manifest:version">
|
| 92 |
+
<ref name="string"/>
|
| 93 |
+
</attribute>
|
| 94 |
+
</optional>
|
| 95 |
+
</interleave>
|
| 96 |
+
</define>
|
| 97 |
+
|
| 98 |
+
<define name="encryption-data">
|
| 99 |
+
<element name="manifest:encryption-data">
|
| 100 |
+
<ref name="encryption-data-attlist"/>
|
| 101 |
+
<ref name="algorithm"/>
|
| 102 |
+
<optional>
|
| 103 |
+
<ref name="start-key-generation"/>
|
| 104 |
+
</optional>
|
| 105 |
+
<ref name="key-derivation"/>
|
| 106 |
+
</element>
|
| 107 |
+
</define>
|
| 108 |
+
<define name="encryption-data-attlist">
|
| 109 |
+
<interleave>
|
| 110 |
+
<attribute name="manifest:checksum-type">
|
| 111 |
+
<choice>
|
| 112 |
+
<value>SHA1/1K</value>
|
| 113 |
+
<ref name="anyURI"/>
|
| 114 |
+
</choice>
|
| 115 |
+
</attribute>
|
| 116 |
+
<attribute name="manifest:checksum">
|
| 117 |
+
<ref name="base64Binary"/>
|
| 118 |
+
</attribute>
|
| 119 |
+
</interleave>
|
| 120 |
+
</define>
|
| 121 |
+
<define name="algorithm">
|
| 122 |
+
<element name="manifest:algorithm">
|
| 123 |
+
<ref name="algorithm-attlist"/>
|
| 124 |
+
<ref name="anyElements"/>
|
| 125 |
+
</element>
|
| 126 |
+
</define>
|
| 127 |
+
<define name="algorithm-attlist">
|
| 128 |
+
<interleave>
|
| 129 |
+
<attribute name="manifest:algorithm-name">
|
| 130 |
+
<choice>
|
| 131 |
+
<value>Blowfish CFB</value>
|
| 132 |
+
<ref name="anyURI"/>
|
| 133 |
+
</choice>
|
| 134 |
+
</attribute>
|
| 135 |
+
<attribute name="manifest:initialisation-vector">
|
| 136 |
+
<ref name="base64Binary"/>
|
| 137 |
+
</attribute>
|
| 138 |
+
</interleave>
|
| 139 |
+
</define>
|
| 140 |
+
<define name="anyAttListOrElements">
|
| 141 |
+
<zeroOrMore>
|
| 142 |
+
<attribute>
|
| 143 |
+
<anyName/>
|
| 144 |
+
<text/>
|
| 145 |
+
</attribute>
|
| 146 |
+
</zeroOrMore>
|
| 147 |
+
<ref name="anyElements"/>
|
| 148 |
+
</define>
|
| 149 |
+
<define name="anyElements">
|
| 150 |
+
<zeroOrMore>
|
| 151 |
+
<element>
|
| 152 |
+
<anyName/>
|
| 153 |
+
<mixed>
|
| 154 |
+
<ref name="anyAttListOrElements"/>
|
| 155 |
+
</mixed>
|
| 156 |
+
</element>
|
| 157 |
+
</zeroOrMore>
|
| 158 |
+
</define>
|
| 159 |
+
<define name="key-derivation">
|
| 160 |
+
<element name="manifest:key-derivation">
|
| 161 |
+
<ref name="key-derivation-attlist"/>
|
| 162 |
+
<empty/>
|
| 163 |
+
</element>
|
| 164 |
+
</define>
|
| 165 |
+
<define name="key-derivation-attlist">
|
| 166 |
+
<interleave>
|
| 167 |
+
<attribute name="manifest:key-derivation-name">
|
| 168 |
+
<choice>
|
| 169 |
+
<value>PBKDF2</value>
|
| 170 |
+
<ref name="anyURI"/>
|
| 171 |
+
</choice>
|
| 172 |
+
</attribute>
|
| 173 |
+
<attribute name="manifest:salt">
|
| 174 |
+
<ref name="base64Binary"/>
|
| 175 |
+
</attribute>
|
| 176 |
+
<attribute name="manifest:iteration-count">
|
| 177 |
+
<ref name="nonNegativeInteger"/>
|
| 178 |
+
</attribute>
|
| 179 |
+
<optional>
|
| 180 |
+
<attribute name="manifest:key-size">
|
| 181 |
+
<ref name="nonNegativeInteger"/>
|
| 182 |
+
</attribute>
|
| 183 |
+
</optional>
|
| 184 |
+
</interleave>
|
| 185 |
+
</define>
|
| 186 |
+
<define name="start-key-generation">
|
| 187 |
+
<element name="manifest:start-key-generation">
|
| 188 |
+
<ref name="start-key-generation-attlist"/>
|
| 189 |
+
<empty/>
|
| 190 |
+
</element>
|
| 191 |
+
</define>
|
| 192 |
+
<define name="start-key-generation-attlist">
|
| 193 |
+
<interleave>
|
| 194 |
+
<attribute name="manifest:start-key-generation-name">
|
| 195 |
+
<choice>
|
| 196 |
+
<value>SHA1</value>
|
| 197 |
+
<ref name="anyURI"/>
|
| 198 |
+
</choice>
|
| 199 |
+
</attribute>
|
| 200 |
+
<optional>
|
| 201 |
+
<attribute name="manifest:key-size">
|
| 202 |
+
<ref name="nonNegativeInteger"/>
|
| 203 |
+
</attribute>
|
| 204 |
+
</optional>
|
| 205 |
+
</interleave>
|
| 206 |
+
</define>
|
| 207 |
+
<define name="base64Binary">
|
| 208 |
+
<data type="base64Binary"/>
|
| 209 |
+
</define>
|
| 210 |
+
<define name="namespacedToken">
|
| 211 |
+
<data type="QName">
|
| 212 |
+
<param name="pattern">[^:]+:[^:]+</param>
|
| 213 |
+
</data>
|
| 214 |
+
</define>
|
| 215 |
+
<define name="nonNegativeInteger">
|
| 216 |
+
<data type="nonNegativeInteger"/>
|
| 217 |
+
</define>
|
| 218 |
+
<define name="string">
|
| 219 |
+
<data type="string"/>
|
| 220 |
+
</define>
|
| 221 |
+
<define name="anyURI">
|
| 222 |
+
<data type="anyURI"/>
|
| 223 |
+
</define>
|
| 224 |
+
</grammar>
|
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-metadata.owl
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
Open Document Format for Office Applications (OpenDocument) Version 1.2
|
| 4 |
+
OASIS Standard, 29 September 2011
|
| 5 |
+
OWL Metadata Manifest Description
|
| 6 |
+
Source: http://docs.oasis-open.org/office/v1.2/os/
|
| 7 |
+
Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
|
| 8 |
+
|
| 9 |
+
All capitalized terms in the following text have the meanings assigned to them
|
| 10 |
+
in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
|
| 11 |
+
full Policy may be found at the OASIS website.
|
| 12 |
+
|
| 13 |
+
This document and translations of it may be copied and furnished to others, and
|
| 14 |
+
derivative works that comment on or otherwise explain it or assist in its
|
| 15 |
+
implementation may be prepared, copied, published, and distributed, in whole or
|
| 16 |
+
in part, without restriction of any kind, provided that the above copyright
|
| 17 |
+
notice and this section are included on all such copies and derivative works.
|
| 18 |
+
However, this document itself may not be modified in any way, including by
|
| 19 |
+
removing the copyright notice or references to OASIS, except as needed for the
|
| 20 |
+
purpose of developing any document or deliverable produced by an OASIS
|
| 21 |
+
Technical Committee (in which case the rules applicable to copyrights, as set
|
| 22 |
+
forth in the OASIS IPR Policy, must be followed) or as required to translate it
|
| 23 |
+
into languages other than English.
|
| 24 |
+
|
| 25 |
+
The limited permissions granted above are perpetual and will not be revoked by
|
| 26 |
+
OASIS or its successors or assigns.
|
| 27 |
+
|
| 28 |
+
This document and the information contained herein is provided on an "AS IS"
|
| 29 |
+
basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
| 30 |
+
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
|
| 31 |
+
INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
|
| 32 |
+
FITNESS FOR A PARTICULAR PURPOSE.
|
| 33 |
+
-->
|
| 34 |
+
|
| 35 |
+
<rdf:RDF
|
| 36 |
+
xmlns="http://www.w3.org/2000/01/rdf-schema#"
|
| 37 |
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
| 38 |
+
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
| 39 |
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
| 40 |
+
xmlns:pkg="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#"
|
| 41 |
+
xmlns:odf="http://docs.oasis-open.org/ns/office/1.2/meta/odf#">
|
| 42 |
+
|
| 43 |
+
<owl:Ontology rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#">
|
| 44 |
+
<owl:imports rdf:resource="http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-package-metadata.owl" />
|
| 45 |
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
|
| 46 |
+
<dc:title xml:lang="en">Open Document Schema Metadata Manifest Ontology</dc:title>
|
| 47 |
+
<label xml:lang="en">ODF Schema Metadata Manifest</label>
|
| 48 |
+
</owl:Ontology>
|
| 49 |
+
|
| 50 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#ContentFile">
|
| 51 |
+
<label xml:lang="en">The OpenDocument Content File</label>
|
| 52 |
+
<comment xml:lang="en">The unique content.xml from the root path of the document</comment>
|
| 53 |
+
<subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
|
| 54 |
+
</owl:Class>
|
| 55 |
+
|
| 56 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#StylesFile">
|
| 57 |
+
<label xml:lang="en">The OpenDocument Styles File</label>
|
| 58 |
+
<comment xml:lang="en">The unique styles.xml from the root path of the document</comment>
|
| 59 |
+
<subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
|
| 60 |
+
</owl:Class>
|
| 61 |
+
|
| 62 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#Element">
|
| 63 |
+
<label xml:lang="en">OpenDocument Package ODF XML Element</label>
|
| 64 |
+
<subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Element"/>
|
| 65 |
+
</owl:Class>
|
| 66 |
+
|
| 67 |
+
<owl:Class rdf:about="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field">
|
| 68 |
+
<label xml:lang="en">OpenDocument Meta field Element</label>
|
| 69 |
+
<subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/odf#Element"/>
|
| 70 |
+
</owl:Class>
|
| 71 |
+
|
| 72 |
+
<owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#prefix">
|
| 73 |
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
|
| 74 |
+
<domain rdf:resource="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field"/>
|
| 75 |
+
<range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
|
| 76 |
+
<label xml:lang="en">has prefix</label>
|
| 77 |
+
</owl:DatatypeProperty>
|
| 78 |
+
|
| 79 |
+
<owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/odf#suffix">
|
| 80 |
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
|
| 81 |
+
<domain rdf:resource="urn:oasis:names:tc:opendocument:xmlns:text:1.0meta-field"/>
|
| 82 |
+
<range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
|
| 83 |
+
<label xml:lang="en">has suffix</label>
|
| 84 |
+
</owl:DatatypeProperty>
|
| 85 |
+
</rdf:RDF>
|
| 86 |
+
|
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-package-metadata.owl
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!--
|
| 3 |
+
Open Document Format for Office Applications (OpenDocument) Version 1.2
|
| 4 |
+
OASIS Standard, 29 September 2011
|
| 5 |
+
OWL Metadata Manifest Description
|
| 6 |
+
Source: http://docs.oasis-open.org/office/v1.2/os/
|
| 7 |
+
Copyright (c) OASIS Open 2002-2011. All Rights Reserved.
|
| 8 |
+
|
| 9 |
+
All capitalized terms in the following text have the meanings assigned to them
|
| 10 |
+
in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The
|
| 11 |
+
full Policy may be found at the OASIS website.
|
| 12 |
+
|
| 13 |
+
This document and translations of it may be copied and furnished to others, and
|
| 14 |
+
derivative works that comment on or otherwise explain it or assist in its
|
| 15 |
+
implementation may be prepared, copied, published, and distributed, in whole or
|
| 16 |
+
in part, without restriction of any kind, provided that the above copyright
|
| 17 |
+
notice and this section are included on all such copies and derivative works.
|
| 18 |
+
However, this document itself may not be modified in any way, including by
|
| 19 |
+
removing the copyright notice or references to OASIS, except as needed for the
|
| 20 |
+
purpose of developing any document or deliverable produced by an OASIS
|
| 21 |
+
Technical Committee (in which case the rules applicable to copyrights, as set
|
| 22 |
+
forth in the OASIS IPR Policy, must be followed) or as required to translate it
|
| 23 |
+
into languages other than English.
|
| 24 |
+
|
| 25 |
+
The limited permissions granted above are perpetual and will not be revoked by
|
| 26 |
+
OASIS or its successors or assigns.
|
| 27 |
+
|
| 28 |
+
This document and the information contained herein is provided on an "AS IS"
|
| 29 |
+
basis and OASIS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
| 30 |
+
LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT
|
| 31 |
+
INFRINGE ANY OWNERSHIP RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR
|
| 32 |
+
FITNESS FOR A PARTICULAR PURPOSE.
|
| 33 |
+
-->
|
| 34 |
+
|
| 35 |
+
<rdf:RDF
|
| 36 |
+
xmlns="http://www.w3.org/2000/01/rdf-schema#"
|
| 37 |
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
| 38 |
+
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
| 39 |
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
| 40 |
+
xmlns:pkg="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#">
|
| 41 |
+
|
| 42 |
+
<owl:Ontology rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#">
|
| 43 |
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
|
| 44 |
+
<dc:title xml:lang="en">Open Document Package Metadata Manifest Ontology</dc:title>
|
| 45 |
+
<label xml:lang="en">ODF Package Metadata Manifest</label>
|
| 46 |
+
</owl:Ontology>
|
| 47 |
+
|
| 48 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document">
|
| 49 |
+
<label xml:lang="en">OpenDocument Document</label>
|
| 50 |
+
</owl:Class>
|
| 51 |
+
|
| 52 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File">
|
| 53 |
+
<label xml:lang="en"> OpenDocument Document Package File</label>
|
| 54 |
+
</owl:Class>
|
| 55 |
+
|
| 56 |
+
<owl:ObjectProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#hasPart">
|
| 57 |
+
<domain rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Document"/>
|
| 58 |
+
<range rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
|
| 59 |
+
<label xml:lang="en">contains</label>
|
| 60 |
+
<comment xml:lang="en">Related to dcterms:hasPart of the Dublin Core Metadata Initiative</comment>
|
| 61 |
+
</owl:ObjectProperty>
|
| 62 |
+
|
| 63 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#MetadataFile">
|
| 64 |
+
<label xml:lang="en">An OpenDocument Metadata File</label>
|
| 65 |
+
<comment xml:lang="en">Used for any metadata file in the document</comment>
|
| 66 |
+
<subClassOf rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
|
| 67 |
+
</owl:Class>
|
| 68 |
+
|
| 69 |
+
<owl:DatatypeProperty rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#mimeType">
|
| 70 |
+
<rdf:type rdf:resource="http://www.w3.org/2002/07/owl#FunctionalProperty"/>
|
| 71 |
+
<domain rdf:resource="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#File"/>
|
| 72 |
+
<range rdf:resource="http://www.w3.org/2001/XMLSchema-datatypes#string"/>
|
| 73 |
+
<label xml:lang="en">the MIME type</label>
|
| 74 |
+
<comment xml:lang="en">A string representing the MIME media type of a file (see RFC4288).</comment>
|
| 75 |
+
</owl:DatatypeProperty>
|
| 76 |
+
|
| 77 |
+
<owl:Class rdf:about="http://docs.oasis-open.org/ns/office/1.2/meta/pkg#Element">
|
| 78 |
+
<label xml:lang="en">OpenDocument Package XML Element</label>
|
| 79 |
+
</owl:Class>
|
| 80 |
+
|
| 81 |
+
</rdf:RDF>
|
tools/jingodf/jingodf/schema/OpenDocument-v1.2-os-schema.rng
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tools/jingodf/setup.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from setuptools import setup
|
| 2 |
+
|
| 3 |
+
setup(name='jingodf',
|
| 4 |
+
version='0.0',
|
| 5 |
+
license='GNU Affero GPL v3',
|
| 6 |
+
author = 'mete0r',
|
| 7 |
+
author_email = 'https://github.com/mete0r',
|
| 8 |
+
packages = ['jingodf'],
|
| 9 |
+
package_data = dict(jingodf=['schema/*']),
|
| 10 |
+
install_requires=['opster == 3.7'],
|
| 11 |
+
entry_points = {
|
| 12 |
+
'console_scripts': ['jingodf = jingodf:main']
|
| 13 |
+
})
|
tools/jxml/jxml.coverage/jxml_coverage.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from __future__ import with_statement
|
| 3 |
+
|
| 4 |
+
import logging
|
| 5 |
+
import sys
|
| 6 |
+
import os.path
|
| 7 |
+
|
| 8 |
+
import colorama
|
| 9 |
+
from colorama import Fore, Back, Style
|
| 10 |
+
from docopt import docopt
|
| 11 |
+
|
| 12 |
+
from jxml.etree import XSLTCoverage
|
| 13 |
+
from jxml.etree import xsltcoverage
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def colorama_init(f):
|
| 17 |
+
def wrapper(*args, **kwargs):
|
| 18 |
+
colorama.init()
|
| 19 |
+
try:
|
| 20 |
+
return f(*args, **kwargs)
|
| 21 |
+
finally:
|
| 22 |
+
colorama.deinit()
|
| 23 |
+
return wrapper
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@colorama_init
|
| 27 |
+
def annotate_main():
|
| 28 |
+
__doc__ = '''
|
| 29 |
+
Usage: jxml-annotate [options] <xml-file>...
|
| 30 |
+
|
| 31 |
+
<xml-file> Cobertura-compatible coverage data file
|
| 32 |
+
--color=[auto|yes|no] Output with colors
|
| 33 |
+
|
| 34 |
+
Example:
|
| 35 |
+
jxml-annotate --color=yes coverage.xml | less -R
|
| 36 |
+
'''
|
| 37 |
+
args = docopt(__doc__)
|
| 38 |
+
|
| 39 |
+
logging.basicConfig()
|
| 40 |
+
logger = logging.getLogger('jxml.annotate')
|
| 41 |
+
|
| 42 |
+
use_color = args['--color'] == 'yes'
|
| 43 |
+
if args['--color'] in ('auto', None):
|
| 44 |
+
use_color = sys.stdout.isatty()
|
| 45 |
+
|
| 46 |
+
coverage = XSLTCoverage()
|
| 47 |
+
for arg in args['<xml-file>']:
|
| 48 |
+
coverage.read_from(arg)
|
| 49 |
+
|
| 50 |
+
traces = coverage.traces
|
| 51 |
+
for filename in sorted(traces):
|
| 52 |
+
covered_lines = traces[filename]
|
| 53 |
+
if not os.path.exists(filename):
|
| 54 |
+
logger.info('skipping %s: not exists', filename)
|
| 55 |
+
continue
|
| 56 |
+
print filename
|
| 57 |
+
|
| 58 |
+
with open(filename) as f:
|
| 59 |
+
for line_no, line in enumerate(f):
|
| 60 |
+
line_no += 1
|
| 61 |
+
count = covered_lines.get(line_no, 0)
|
| 62 |
+
annotated = '%8d: %s' % (count, line)
|
| 63 |
+
|
| 64 |
+
if use_color:
|
| 65 |
+
if count == 0:
|
| 66 |
+
color = Fore.RED
|
| 67 |
+
else:
|
| 68 |
+
color = Fore.RESET
|
| 69 |
+
annotated = color + annotated + Fore.RESET
|
| 70 |
+
sys.stdout.write(annotated)
|
| 71 |
+
|
| 72 |
+
print ''
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def load_tests(filenames):
|
| 76 |
+
import unittest
|
| 77 |
+
ts = unittest.TestSuite()
|
| 78 |
+
testloader = unittest.defaultTestLoader
|
| 79 |
+
for filename in filenames:
|
| 80 |
+
d = dict()
|
| 81 |
+
execfile(filename, d)
|
| 82 |
+
for name in d:
|
| 83 |
+
x = d[name]
|
| 84 |
+
if isinstance(x, type) and issubclass(x, unittest.TestCase):
|
| 85 |
+
ts.addTests(testloader.loadTestsFromTestCase(x))
|
| 86 |
+
return ts
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def cov_test_main():
|
| 90 |
+
__doc__ = '''
|
| 91 |
+
Usage: jxml-cov-test [options] <output-file> <unittest-file>...
|
| 92 |
+
|
| 93 |
+
<output-file> Cobertura-compatible coverage data file.
|
| 94 |
+
<unittest-file> unittest files.
|
| 95 |
+
|
| 96 |
+
Example:
|
| 97 |
+
jxml-cov-test coverage.xml test1.py test2.py
|
| 98 |
+
'''
|
| 99 |
+
args = docopt(__doc__)
|
| 100 |
+
|
| 101 |
+
logging.basicConfig()
|
| 102 |
+
logger = logging.getLogger('jxml.cov-test')
|
| 103 |
+
|
| 104 |
+
from java.lang import System
|
| 105 |
+
import unittest
|
| 106 |
+
|
| 107 |
+
props = System.getProperties()
|
| 108 |
+
props['javax.xml.transform.TransformerFactory'] = 'org.apache.xalan.processor.TransformerFactoryImpl'
|
| 109 |
+
props['javax.xml.parsers.DocumentBuilderFactory'] = 'org.apache.xerces.jaxp.DocumentBuilderFactoryImpl'
|
| 110 |
+
props['javax.xml.parsers.SAXParserFactory'] = 'org.apache.xerces.jaxp.SAXParserFactoryImpl'
|
| 111 |
+
|
| 112 |
+
output_name = args['<output-file>']
|
| 113 |
+
test_filenames = args['<unittest-file>']
|
| 114 |
+
ts = load_tests(test_filenames)
|
| 115 |
+
runner = unittest.TextTestRunner()
|
| 116 |
+
with xsltcoverage(output_name) as coverage:
|
| 117 |
+
runner.run(ts)
|
tools/jxml/jxml.coverage/setup.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from setuptools import setup, find_packages
|
| 3 |
+
import os.path
|
| 4 |
+
import sys
|
| 5 |
+
if not sys.platform.startswith('java'):
|
| 6 |
+
raise SystemExit(1)
|
| 7 |
+
|
| 8 |
+
os.chdir(os.path.dirname(__file__))
|
| 9 |
+
|
| 10 |
+
console_scripts = ['jxml-annotate = jxml_coverage:annotate_main',
|
| 11 |
+
'jxml-cov-test = jxml_coverage:cov_test_main']
|
| 12 |
+
|
| 13 |
+
setup(name='jxml.coverage',
|
| 14 |
+
py_modules=['jxml_coverage'],
|
| 15 |
+
install_requires=['jxml', 'docopt', 'colorama'],
|
| 16 |
+
entry_points=dict(console_scripts=console_scripts))
|
tools/jxml/jxml/__init__.py
ADDED
|
File without changes
|
tools/jxml/jxml/etree/__init__.py
ADDED
|
@@ -0,0 +1,1072 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8
|
| 2 |
+
from __future__ import with_statement
|
| 3 |
+
|
| 4 |
+
from contextlib import contextmanager
|
| 5 |
+
import logging
|
| 6 |
+
from itertools import imap
|
| 7 |
+
from itertools import ifilter
|
| 8 |
+
from itertools import takewhile
|
| 9 |
+
import os.path
|
| 10 |
+
from urlparse import urlparse
|
| 11 |
+
from urlparse import urljoin
|
| 12 |
+
from pkgutil import simplegeneric
|
| 13 |
+
|
| 14 |
+
from javax.xml import XMLConstants
|
| 15 |
+
from javax.xml.parsers import DocumentBuilderFactory
|
| 16 |
+
from javax.xml.xpath import XPathFactory
|
| 17 |
+
from javax.xml.xpath import XPathConstants
|
| 18 |
+
from javax.xml.xpath import XPathExpressionException
|
| 19 |
+
from javax.xml.namespace import NamespaceContext
|
| 20 |
+
from javax.xml.transform import OutputKeys
|
| 21 |
+
from javax.xml.transform import TransformerFactory
|
| 22 |
+
from javax.xml.transform import URIResolver
|
| 23 |
+
from javax.xml.transform.dom import DOMSource
|
| 24 |
+
from javax.xml.transform.dom import DOMResult
|
| 25 |
+
from javax.xml.transform.stream import StreamSource
|
| 26 |
+
from javax.xml.transform.stream import StreamResult
|
| 27 |
+
from org.w3c import dom
|
| 28 |
+
from java.io import File
|
| 29 |
+
from java.io import InputStream
|
| 30 |
+
from java.io import IOException
|
| 31 |
+
from java.io import FileInputStream
|
| 32 |
+
from java.io import FileOutputStream
|
| 33 |
+
from java.io import ByteArrayInputStream
|
| 34 |
+
from java.io import ByteArrayOutputStream
|
| 35 |
+
from java.io import ByteArrayInputStream
|
| 36 |
+
from java.util import Iterator
|
| 37 |
+
from java.util import NoSuchElementException
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
logger = logging.getLogger(__name__)
|
| 41 |
+
|
| 42 |
+
fac = DocumentBuilderFactory.newInstance()
|
| 43 |
+
fac.setNamespaceAware(True)
|
| 44 |
+
builder = fac.newDocumentBuilder()
|
| 45 |
+
transformfac = TransformerFactory.newInstance()
|
| 46 |
+
xpathfac = XPathFactory.newInstance()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class XSLTCoverage(object):
|
| 50 |
+
|
| 51 |
+
def __init__(self):
|
| 52 |
+
self.traces = dict()
|
| 53 |
+
|
| 54 |
+
def trace(self, systemId, startline, endline):
|
| 55 |
+
files = self.traces
|
| 56 |
+
lines = files.setdefault(systemId, dict())
|
| 57 |
+
lines[startline] = lines.get(startline, 0) + 1
|
| 58 |
+
lines[endline] = lines.get(endline, 0) + 1
|
| 59 |
+
|
| 60 |
+
def writeto(self, f):
|
| 61 |
+
trace_root = Element('coverage')
|
| 62 |
+
packages = SubElement(trace_root, 'packages')
|
| 63 |
+
package_attrib = {'name': '', 'branch-rate': '0', 'complexity': '0',
|
| 64 |
+
'line-rate': '0'}
|
| 65 |
+
package = SubElement(packages, 'package', package_attrib)
|
| 66 |
+
classes = SubElement(package, 'classes')
|
| 67 |
+
|
| 68 |
+
for filename in sorted(self.traces):
|
| 69 |
+
if not filename:
|
| 70 |
+
continue
|
| 71 |
+
if filename.startswith('file://'):
|
| 72 |
+
fn = filename[len('file://'):]
|
| 73 |
+
else:
|
| 74 |
+
fn = filename
|
| 75 |
+
class_attrib = dict()
|
| 76 |
+
class_attrib['filename'] = fn
|
| 77 |
+
class_attrib['branch-rate'] = '0'
|
| 78 |
+
class_attrib['complexity'] = '0'
|
| 79 |
+
class_attrib['line-rate'] = '0'
|
| 80 |
+
class_attrib['name'] = os.path.basename(fn)
|
| 81 |
+
clas = SubElement(classes, 'class', class_attrib)
|
| 82 |
+
methods = SubElement(clas, 'methods')
|
| 83 |
+
linesElem = SubElement(clas, 'lines')
|
| 84 |
+
lines = self.traces[filename]
|
| 85 |
+
for lineno in sorted(lines):
|
| 86 |
+
count = lines[lineno]
|
| 87 |
+
lineElem = SubElement(linesElem, 'line',
|
| 88 |
+
dict(number=str(lineno),
|
| 89 |
+
hits=str(count)))
|
| 90 |
+
s = tostring(trace_root, encoding='utf-8', xml_declaration=True)
|
| 91 |
+
f.write(s)
|
| 92 |
+
f.write('\n')
|
| 93 |
+
|
| 94 |
+
def read_from(self, filename):
|
| 95 |
+
tree = parse(filename)
|
| 96 |
+
coverage = tree.getroot()
|
| 97 |
+
assert coverage.tag == 'coverage'
|
| 98 |
+
packages = coverage[0]
|
| 99 |
+
assert packages.tag == 'packages'
|
| 100 |
+
for package in packages:
|
| 101 |
+
if package.tag != 'package':
|
| 102 |
+
continue
|
| 103 |
+
classes = package[0]
|
| 104 |
+
assert classes.tag == 'classes'
|
| 105 |
+
for clas in classes:
|
| 106 |
+
if clas.tag != 'class':
|
| 107 |
+
continue
|
| 108 |
+
filename = clas.get('filename')
|
| 109 |
+
trace = self.traces.setdefault(filename, dict())
|
| 110 |
+
for lines in clas:
|
| 111 |
+
if lines.tag != 'lines':
|
| 112 |
+
continue
|
| 113 |
+
for line in lines:
|
| 114 |
+
if line.tag != 'line':
|
| 115 |
+
continue
|
| 116 |
+
lineno = int(line.get('number'))
|
| 117 |
+
hits = int(line.get('hits'))
|
| 118 |
+
trace[lineno] = trace.get(lineno, 0) + hits
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
xsltcoverage_trace = None
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@contextmanager
|
| 125 |
+
def xsltcoverage(output=None):
|
| 126 |
+
xsltcoverage = XSLTCoverage()
|
| 127 |
+
globals()['xsltcoverage_trace'] = xsltcoverage
|
| 128 |
+
yield xsltcoverage
|
| 129 |
+
globals()['xsltcoverage_trace'] = None
|
| 130 |
+
if output is not None:
|
| 131 |
+
if isinstance(output, basestring):
|
| 132 |
+
with open(output, 'w') as f:
|
| 133 |
+
xsltcoverage.writeto(f)
|
| 134 |
+
elif hasattr(output, 'write'):
|
| 135 |
+
xsltcoverage.writeto(output)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def instrument_xalan_transformer_factory(transformfac):
|
| 139 |
+
from javax.xml.transform import Templates
|
| 140 |
+
try:
|
| 141 |
+
from org.apache.xalan.processor import TransformerFactoryImpl
|
| 142 |
+
from org.apache.xalan.transformer import TransformerImpl
|
| 143 |
+
from org.apache.xalan.trace import TraceListenerEx2
|
| 144 |
+
except ImportError:
|
| 145 |
+
logger.warning('Xalan-J is not found: '
|
| 146 |
+
'check your CLASSPATH. '
|
| 147 |
+
'there will be no coverage data')
|
| 148 |
+
return transformfac
|
| 149 |
+
if not isinstance(transformfac, TransformerFactoryImpl):
|
| 150 |
+
logger.warning('TransformerFactory implementation is not Xalan-J: '
|
| 151 |
+
'check system property of '
|
| 152 |
+
'javax.xml.transform.TransformerFactory. '
|
| 153 |
+
'there will be no coverage data')
|
| 154 |
+
return transformfac
|
| 155 |
+
if xsltcoverage_trace is None:
|
| 156 |
+
return transformfac
|
| 157 |
+
|
| 158 |
+
class XalanTraceListener(TraceListenerEx2):
|
| 159 |
+
|
| 160 |
+
def trace(self, ev):
|
| 161 |
+
stylenode = ev.m_styleNode
|
| 162 |
+
systemId = stylenode.getSystemId()
|
| 163 |
+
startline = stylenode.getLineNumber()
|
| 164 |
+
endline = stylenode.getEndLineNumber()
|
| 165 |
+
|
| 166 |
+
xsltcoverage_trace.trace(systemId, startline, endline)
|
| 167 |
+
|
| 168 |
+
traceListener = XalanTraceListener()
|
| 169 |
+
|
| 170 |
+
class XalanTransformerInstrumentImpl(TransformerFactoryImpl):
|
| 171 |
+
|
| 172 |
+
def newTemplates(self, source):
|
| 173 |
+
templates = TransformerFactoryImpl.newTemplates(self, source)
|
| 174 |
+
return XalanTemplates(templates, source)
|
| 175 |
+
|
| 176 |
+
def newTransformer(self, source=None):
|
| 177 |
+
impl = TransformerFactoryImpl.newTransformer(self, source)
|
| 178 |
+
# add listener
|
| 179 |
+
tracemanager = impl.getTraceManager()
|
| 180 |
+
tracemanager.addTraceListener(traceListener)
|
| 181 |
+
return impl
|
| 182 |
+
|
| 183 |
+
class XalanTemplates(Templates):
|
| 184 |
+
|
| 185 |
+
def __init__(self, t, s):
|
| 186 |
+
self.templates = t
|
| 187 |
+
self.source = s
|
| 188 |
+
|
| 189 |
+
def newTransformer(self):
|
| 190 |
+
transformer = self.templates.newTransformer()
|
| 191 |
+
# add listener
|
| 192 |
+
tracemanager = transformer.getTraceManager()
|
| 193 |
+
tracemanager.addTraceListener(traceListener)
|
| 194 |
+
return transformer
|
| 195 |
+
|
| 196 |
+
def getOutputProperties(self):
|
| 197 |
+
return self.templates.getOutputProperties()
|
| 198 |
+
|
| 199 |
+
return XalanTransformerInstrumentImpl()
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
XML_URI = 'http://www.w3.org/XML/1998/namespace'
|
| 203 |
+
XMLNS_URI = 'http://www.w3.org/2000/xmlns/'
|
| 204 |
+
TRANSIENT_NAMESPACE_PREFIXES = {'xml': XML_URI, 'xmlns': XMLNS_URI}
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def split_prefix_and_localName(tag):
|
| 208 |
+
if ':' in tag:
|
| 209 |
+
return tag.split(':', 1)
|
| 210 |
+
else:
|
| 211 |
+
return None, tag
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
def split_jcnotation(tag):
|
| 215 |
+
''' Get namespaceURI and localName from a James Clark's notation.
|
| 216 |
+
|
| 217 |
+
See http://www.jclark.com/xml/xmlns.htm
|
| 218 |
+
'''
|
| 219 |
+
if tag[0] != '{':
|
| 220 |
+
return None, tag
|
| 221 |
+
pos = tag.find('}')
|
| 222 |
+
if pos == -1:
|
| 223 |
+
raise ValueError(tag)
|
| 224 |
+
return tag[1:pos], tag[pos+1:]
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def get_prefix_for_uri(namespaceURI, nsmap):
|
| 228 |
+
for prefix, uri in nsmap.items():
|
| 229 |
+
if uri == namespaceURI:
|
| 230 |
+
return prefix
|
| 231 |
+
if namespaceURI in TRANSIENT_NAMESPACE_PREFIXES:
|
| 232 |
+
return TRANSIENT_NAMESPACE_PREFIXES[namespaceURI]
|
| 233 |
+
prefix = 'ns' + str(len(TRANSIENT_NAMESPACE_PREFIXES))
|
| 234 |
+
TRANSIENT_NAMESPACE_PREFIXES[namespaceURI] = prefix
|
| 235 |
+
return prefix
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def dom_element_from_tag(tag, nsmap=None):
|
| 239 |
+
if nsmap is None:
|
| 240 |
+
nsmap = {}
|
| 241 |
+
doc = builder.newDocument()
|
| 242 |
+
namespaceURI, localName = split_jcnotation(tag)
|
| 243 |
+
if namespaceURI is not None:
|
| 244 |
+
prefix = get_prefix_for_uri(namespaceURI, nsmap)
|
| 245 |
+
return doc.createElementNS(namespaceURI, prefix + ':' + localName)
|
| 246 |
+
else:
|
| 247 |
+
return doc.createElement(localName)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def dom_document_from_file(f):
|
| 251 |
+
if hasattr(f, 'name'):
|
| 252 |
+
documentURI = os.path.abspath(f.name)
|
| 253 |
+
else:
|
| 254 |
+
documentURI = None
|
| 255 |
+
bytestring = f.read()
|
| 256 |
+
|
| 257 |
+
temp = File.createTempFile('someprefix', 'tmp')
|
| 258 |
+
|
| 259 |
+
outputstream = FileOutputStream(temp)
|
| 260 |
+
try:
|
| 261 |
+
outputstream.write(bytestring)
|
| 262 |
+
finally:
|
| 263 |
+
outputstream.close()
|
| 264 |
+
|
| 265 |
+
inputstream = FileInputStream(temp)
|
| 266 |
+
try:
|
| 267 |
+
dom_doc = dom_document_from_inputstream(inputstream)
|
| 268 |
+
dom_doc.setDocumentURI(documentURI)
|
| 269 |
+
return dom_doc
|
| 270 |
+
finally:
|
| 271 |
+
inputstream.close()
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def dom_document_from_inputstream(inputstream):
|
| 275 |
+
return builder.parse(inputstream)
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def dom_tag(dom_element):
|
| 279 |
+
return dom_jcnotation(dom_element)
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def dom_jcnotation(dom_node):
|
| 283 |
+
''' Get node name expressed in James Clark's notation.
|
| 284 |
+
|
| 285 |
+
See http://www.jclark.com/xml/xmlns.htm
|
| 286 |
+
'''
|
| 287 |
+
namespaceURI = dom_node.namespaceURI
|
| 288 |
+
if namespaceURI:
|
| 289 |
+
namespace = '{%s}' % namespaceURI
|
| 290 |
+
return namespace + dom_node.localName
|
| 291 |
+
else:
|
| 292 |
+
return dom_node.nodeName
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
def dom_nsmap_specified_here(dom_element):
|
| 296 |
+
nsmap = {}
|
| 297 |
+
attrs = dom_element.attributes
|
| 298 |
+
for i in range(0, attrs.length):
|
| 299 |
+
attr = attrs.item(i)
|
| 300 |
+
if attr.name.startswith('xmlns:'):
|
| 301 |
+
prefix = attr.name[len('xmlns:'):]
|
| 302 |
+
uri = attr.value
|
| 303 |
+
nsmap[prefix] = uri
|
| 304 |
+
else:
|
| 305 |
+
prefix = attr.prefix
|
| 306 |
+
uri = attr.namespaceURI
|
| 307 |
+
if prefix and uri:
|
| 308 |
+
nsmap[prefix] = uri
|
| 309 |
+
return nsmap
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
dom_node_is_element = lambda n: n.nodeType == dom.Node.ELEMENT_NODE
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def dom_ancestors(dom_node):
|
| 316 |
+
while True:
|
| 317 |
+
dom_node = dom_node.parentNode
|
| 318 |
+
if dom_node:
|
| 319 |
+
yield dom_node
|
| 320 |
+
else:
|
| 321 |
+
return
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def dom_ancestor_elements(dom_node):
|
| 325 |
+
return takewhile(dom_node_is_element,
|
| 326 |
+
dom_ancestors(dom_node))
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def dom_siblings_next(dom_node):
|
| 330 |
+
while True:
|
| 331 |
+
dom_node = dom_node.nextSibling
|
| 332 |
+
if dom_node:
|
| 333 |
+
yield dom_node
|
| 334 |
+
else:
|
| 335 |
+
return
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
def dom_siblings_previous(dom_node):
|
| 339 |
+
while True:
|
| 340 |
+
dom_node = dom_node.previousSibling
|
| 341 |
+
if dom_node:
|
| 342 |
+
yield dom_node
|
| 343 |
+
else:
|
| 344 |
+
return
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def dom_nsmap(dom_element):
|
| 348 |
+
assert dom_element.nodeType == dom_element.ELEMENT_NODE
|
| 349 |
+
default_nsmap = dom_nsmap_specified_here(dom_element)
|
| 350 |
+
for ancestor in dom_ancestor_elements(dom_element):
|
| 351 |
+
nsmap = dom_nsmap_specified_here(ancestor)
|
| 352 |
+
nsmap.update(default_nsmap)
|
| 353 |
+
default_nsmap = nsmap
|
| 354 |
+
return default_nsmap
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
def dom_set_attr(dom_element, key, value, nsmap):
|
| 358 |
+
namespaceURI, localName = split_jcnotation(key)
|
| 359 |
+
if namespaceURI:
|
| 360 |
+
prefix = get_prefix_for_uri(namespaceURI, nsmap)
|
| 361 |
+
qualifiedName = prefix + ':' + localName
|
| 362 |
+
dom_element.setAttributeNS(namespaceURI, qualifiedName, value)
|
| 363 |
+
else:
|
| 364 |
+
dom_element.setAttribute(key, value)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def Element(tag, attrib=None, nsmap=None, **extra):
|
| 368 |
+
assert isinstance(tag, basestring)
|
| 369 |
+
dom_element = dom_element_from_tag(tag, nsmap)
|
| 370 |
+
nsmap = nsmap or {}
|
| 371 |
+
|
| 372 |
+
dom_document = dom_element.getOwnerDocument()
|
| 373 |
+
for prefix, namespaceURI in nsmap.items():
|
| 374 |
+
dom_element.setAttributeNS(XMLNS_URI, 'xmlns:'+prefix, namespaceURI)
|
| 375 |
+
|
| 376 |
+
if attrib:
|
| 377 |
+
extra.update(attrib)
|
| 378 |
+
if extra:
|
| 379 |
+
for k, v in extra.items():
|
| 380 |
+
dom_set_attr(dom_element, k, v, nsmap)
|
| 381 |
+
return _Element(dom_element)
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def SubElement(parent, tag, attrib=None, nsmap=None, **extra):
|
| 385 |
+
element = Element(tag, attrib, nsmap, **extra)
|
| 386 |
+
parent.append(element)
|
| 387 |
+
return element
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
def ElementTree(element=None, file=None, parser=None):
|
| 391 |
+
if parser is not None:
|
| 392 |
+
raise NotImplementedError('parser is not supported')
|
| 393 |
+
elif element is not None:
|
| 394 |
+
dom_doc = builder.newDocument()
|
| 395 |
+
dom_doc.documentURI = element._dom_element.ownerDocument.documentURI
|
| 396 |
+
dom_root = dom_doc.importNode(element._dom_element, True)
|
| 397 |
+
dom_doc.appendChild(dom_root)
|
| 398 |
+
_Element(dom_root)
|
| 399 |
+
elif file is not None:
|
| 400 |
+
dom_doc = dom_document_from_file(file)
|
| 401 |
+
else:
|
| 402 |
+
raise ValueError('element and file is None')
|
| 403 |
+
return _ElementTree(dom_doc)
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
class _Element(object):
|
| 407 |
+
|
| 408 |
+
def __init__(self, dom_element):
|
| 409 |
+
if dom_element.nodeType != dom_element.ELEMENT_NODE:
|
| 410 |
+
raise ValueError('ELEMENT_NODE(%d) required: %d' %
|
| 411 |
+
(dom_element.ELEMENT_NODE, dom_element.nodeType))
|
| 412 |
+
self._dom_element = dom_element
|
| 413 |
+
|
| 414 |
+
def getroottree(self):
|
| 415 |
+
dom_doc = self._dom_element.getOwnerDocument()
|
| 416 |
+
return _ElementTree(dom_doc)
|
| 417 |
+
|
| 418 |
+
@property
|
| 419 |
+
def attrib(self):
|
| 420 |
+
attrib = dict()
|
| 421 |
+
dom_attrib = self._dom_element.getAttributes()
|
| 422 |
+
for i in range(dom_attrib.length):
|
| 423 |
+
child = dom_attrib.item(i)
|
| 424 |
+
name = child.name
|
| 425 |
+
if not name.startswith('xmlns:') and name != 'xmlns':
|
| 426 |
+
attrib[dom_jcnotation(child)] = child.value
|
| 427 |
+
return attrib
|
| 428 |
+
|
| 429 |
+
@property
|
| 430 |
+
def tag(self):
|
| 431 |
+
return dom_tag(self._dom_element)
|
| 432 |
+
|
| 433 |
+
@property
|
| 434 |
+
def nsmap(self):
|
| 435 |
+
return dom_nsmap(self._dom_element)
|
| 436 |
+
|
| 437 |
+
@property
|
| 438 |
+
def prefix(self):
|
| 439 |
+
namespaceURI = self._dom_element.namespaceURI
|
| 440 |
+
return self._dom_element.lookupPrefix(namespaceURI)
|
| 441 |
+
|
| 442 |
+
@property
|
| 443 |
+
def text(self):
|
| 444 |
+
node = self._dom_element.firstChild
|
| 445 |
+
if node and node.nodeType == node.TEXT_NODE:
|
| 446 |
+
return node.getWholeText()
|
| 447 |
+
|
| 448 |
+
@property
|
| 449 |
+
def tail(self):
|
| 450 |
+
node = self._dom_element.nextSibling
|
| 451 |
+
if node and node.nodeType == node.TEXT_NODE:
|
| 452 |
+
return node.getWholeText()
|
| 453 |
+
|
| 454 |
+
@property
|
| 455 |
+
def base(self):
|
| 456 |
+
dom_doc = self._dom_element.ownerDocument
|
| 457 |
+
return dom_doc.documentURI
|
| 458 |
+
|
| 459 |
+
def __deepcopy__(self, memo):
|
| 460 |
+
dom_doc = builder.newDocument()
|
| 461 |
+
copied = dom_doc.importNode(self._dom_element, True)
|
| 462 |
+
return _Element(copied)
|
| 463 |
+
|
| 464 |
+
def __iter__(self):
|
| 465 |
+
childs = self._dom_element.getChildNodes()
|
| 466 |
+
for i in range(childs.length):
|
| 467 |
+
child = childs.item(i)
|
| 468 |
+
if child.nodeType == child.ELEMENT_NODE:
|
| 469 |
+
yield _Element(child)
|
| 470 |
+
|
| 471 |
+
def __getitem__(self, index):
|
| 472 |
+
if index >= 0:
|
| 473 |
+
iterable = self.__iter__()
|
| 474 |
+
else:
|
| 475 |
+
iterable = self.__reversed__()
|
| 476 |
+
index = -index - 1
|
| 477 |
+
for child in iterable:
|
| 478 |
+
if index == 0:
|
| 479 |
+
return child
|
| 480 |
+
index -= 1
|
| 481 |
+
raise IndexError(index)
|
| 482 |
+
|
| 483 |
+
def __len__(self):
|
| 484 |
+
n = 0
|
| 485 |
+
for _ in self.__iter__():
|
| 486 |
+
n += 1
|
| 487 |
+
return n
|
| 488 |
+
|
| 489 |
+
def __reversed__(self):
|
| 490 |
+
childs = self._dom_element.getChildNodes()
|
| 491 |
+
length = childs.length
|
| 492 |
+
for x in range(length):
|
| 493 |
+
i = length - x - 1
|
| 494 |
+
child = childs.item(i)
|
| 495 |
+
if child.nodeType == child.ELEMENT_NODE:
|
| 496 |
+
yield _Element(child)
|
| 497 |
+
|
| 498 |
+
def __setitem__(self, index, new_elem):
|
| 499 |
+
old_elem = self.__getitem__(index)
|
| 500 |
+
|
| 501 |
+
dom_document = self._dom_element.getOwnerDocument()
|
| 502 |
+
dom_element_new = dom_document.importNode(new_elem._dom_element, True)
|
| 503 |
+
self._dom_element.replaceChild(dom_element_new,
|
| 504 |
+
old_elem._dom_element)
|
| 505 |
+
|
| 506 |
+
def append(self, element):
|
| 507 |
+
dom_document = self._dom_element.getOwnerDocument()
|
| 508 |
+
dom_element_new = dom_document.importNode(element._dom_element, True)
|
| 509 |
+
self._dom_element.appendChild(dom_element_new)
|
| 510 |
+
element._dom_element = dom_element_new
|
| 511 |
+
|
| 512 |
+
def get(self, key):
|
| 513 |
+
return self.attrib.get(key)
|
| 514 |
+
|
| 515 |
+
def getchildren(self):
|
| 516 |
+
# TODO: deprecated warning
|
| 517 |
+
return list(self.__iter__())
|
| 518 |
+
|
| 519 |
+
def getnext(self):
|
| 520 |
+
sibling = None
|
| 521 |
+
for sibling in dom_siblings_next(self._dom_element):
|
| 522 |
+
if dom_node_is_element(sibling):
|
| 523 |
+
return _Element(sibling)
|
| 524 |
+
|
| 525 |
+
def getparent(self):
|
| 526 |
+
dom_parent = self._dom_element.getParentNode()
|
| 527 |
+
if dom_parent and dom_node_is_element(dom_parent):
|
| 528 |
+
return _Element(dom_parent)
|
| 529 |
+
|
| 530 |
+
def getprevious(self):
|
| 531 |
+
sibling = None
|
| 532 |
+
for sibling in dom_siblings_previous(self._dom_element):
|
| 533 |
+
if dom_node_is_element(sibling):
|
| 534 |
+
return _Element(sibling)
|
| 535 |
+
|
| 536 |
+
def iterancestors(self, *tags):
|
| 537 |
+
parents = imap(_Element, dom_ancestor_elements(self._dom_element))
|
| 538 |
+
if tags:
|
| 539 |
+
parents = ifilter(lambda elem: elem.tag in tags,
|
| 540 |
+
parents)
|
| 541 |
+
return parents
|
| 542 |
+
|
| 543 |
+
def keys(self):
|
| 544 |
+
return self.attrib.keys()
|
| 545 |
+
|
| 546 |
+
def set(self, key, value):
|
| 547 |
+
dom_doc = self._dom_element.getOwnerDocument()
|
| 548 |
+
dom_element = self._dom_element
|
| 549 |
+
dom_set_attr(dom_element, key, value, self.nsmap)
|
| 550 |
+
|
| 551 |
+
def values(self):
|
| 552 |
+
return self.attrib.values()
|
| 553 |
+
|
| 554 |
+
def xpath(self, _path, namespaces=None, extensions=None,
|
| 555 |
+
smart_strings=True, **_variables):
|
| 556 |
+
xpath = XPath(_path, namespaces, extensions, smart_strings)
|
| 557 |
+
return xpath(self, **_variables)
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
class _ElementTree(object):
|
| 561 |
+
|
| 562 |
+
def __init__(self, dom_doc):
|
| 563 |
+
self._set_dom_doc(dom_doc)
|
| 564 |
+
|
| 565 |
+
def __copy__(self, dom_doc):
|
| 566 |
+
raise NotImplementedError('__copy__() is not implemented yet')
|
| 567 |
+
|
| 568 |
+
def __deepcopy__(self, memo):
|
| 569 |
+
dom_doc = builder.newDocument()
|
| 570 |
+
dom_root = dom_doc.importNode(self.getroot()._dom_element, True)
|
| 571 |
+
return _ElementTree(dom_doc)
|
| 572 |
+
|
| 573 |
+
@property
|
| 574 |
+
def docinfo(self):
|
| 575 |
+
dom_doc = self._dom_doc
|
| 576 |
+
return DocInfo(dom_doc)
|
| 577 |
+
|
| 578 |
+
def _setroot(self, root):
|
| 579 |
+
self._set_dom_doc(root._dom_element.getOwnerDocument())
|
| 580 |
+
|
| 581 |
+
def _set_dom_doc(self, dom_doc):
|
| 582 |
+
if dom_doc.nodeType != dom_doc.DOCUMENT_NODE:
|
| 583 |
+
raise ValueError('DOCUMENT_NODE(%d) required: %d' %
|
| 584 |
+
(dom.DOCUMENT_NODE, dom_doc.nodeType))
|
| 585 |
+
self._dom_doc = dom_doc
|
| 586 |
+
|
| 587 |
+
def getroot(self):
|
| 588 |
+
dom_root = self._dom_doc.getDocumentElement()
|
| 589 |
+
if dom_root is not None:
|
| 590 |
+
return _Element(dom_root)
|
| 591 |
+
|
| 592 |
+
def find(self, path, namespaces=None):
|
| 593 |
+
root = self.getroot()
|
| 594 |
+
return root.find(path, namespaces)
|
| 595 |
+
|
| 596 |
+
def findall(self, path, namespaces=None):
|
| 597 |
+
root = self.getroot()
|
| 598 |
+
return root.findall(path, namespaces)
|
| 599 |
+
|
| 600 |
+
def findtext(self, path, default=None, namespaces=None):
|
| 601 |
+
root = self.getroot()
|
| 602 |
+
return root.findtext(path, default, namespaces)
|
| 603 |
+
|
| 604 |
+
def iter(self, **tags):
|
| 605 |
+
root = self.getroot()
|
| 606 |
+
return root.iter(*tags)
|
| 607 |
+
|
| 608 |
+
def iterfind(self, path, namespaces=None):
|
| 609 |
+
root = self.getroot()
|
| 610 |
+
return root.iterfind(path)
|
| 611 |
+
|
| 612 |
+
def relaxng(self, relaxng):
|
| 613 |
+
raise NotImplementedError('relaxng is not implemented yet')
|
| 614 |
+
|
| 615 |
+
def write(self, file, encoding=None, method='xml', pretty_print=False,
|
| 616 |
+
xml_declaration=None, with_tail=True, standalone=None,
|
| 617 |
+
compression=0, exclusive=False, with_comments=True,
|
| 618 |
+
inclusive_ns_prefixes=None):
|
| 619 |
+
|
| 620 |
+
if compression != 0:
|
| 621 |
+
raise NotImplementedError('compression should be 0')
|
| 622 |
+
|
| 623 |
+
s = tostring(self, encoding=encoding, method=method,
|
| 624 |
+
pretty_print=pretty_print,
|
| 625 |
+
xml_declaration=xml_declaration, with_tail=with_tail,
|
| 626 |
+
standalone=standalone, exclusive=exclusive,
|
| 627 |
+
with_comments=with_comments,
|
| 628 |
+
inclusive_ns_prefixes=inclusive_ns_prefixes)
|
| 629 |
+
if isinstance(file, basestring):
|
| 630 |
+
with open(file, 'w') as f:
|
| 631 |
+
f.write(s)
|
| 632 |
+
elif hasattr(file, 'write'):
|
| 633 |
+
file.write(s)
|
| 634 |
+
|
| 635 |
+
def write_c14n(self, file, exlusive=False, with_comments=True,
|
| 636 |
+
compression=0, inclusive_ns_prefixes=None):
|
| 637 |
+
raise NotImplementedError('write_c14n() is not implemented yet')
|
| 638 |
+
|
| 639 |
+
def xinclude(self):
|
| 640 |
+
raise NotImplementedError('xinclude() is not implemented yet')
|
| 641 |
+
|
| 642 |
+
def xmlschema(self, xmlschema):
|
| 643 |
+
raise NotImplementedError('xmlschema() is not implemented yet')
|
| 644 |
+
|
| 645 |
+
def xpath(self, _path, namespaces=None, extensions=None,
|
| 646 |
+
smart_strings=True, **_variables):
|
| 647 |
+
xpath = XPath(_path, namespaces, extensions, smart_strings)
|
| 648 |
+
return xpath(self, **_variables)
|
| 649 |
+
|
| 650 |
+
def xslt(self, _xslt, extensions=None, access_control=None, **_kw):
|
| 651 |
+
xslt = XSLT(_xslt, extensions=extensions,
|
| 652 |
+
access_control=access_control)
|
| 653 |
+
return xslt(self, **_kw)
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
class DocInfo(object):
|
| 657 |
+
|
| 658 |
+
def __init__(self, dom_doc):
|
| 659 |
+
self.dom_doc = dom_doc
|
| 660 |
+
|
| 661 |
+
def getURL(self):
|
| 662 |
+
return self.dom_doc.documentURI
|
| 663 |
+
|
| 664 |
+
def setURL(self, uri):
|
| 665 |
+
self.dom_doc.documentURI = uri
|
| 666 |
+
|
| 667 |
+
URL = property(getURL, setURL)
|
| 668 |
+
|
| 669 |
+
@property
|
| 670 |
+
def doctype(self):
|
| 671 |
+
doctype = self.dom_doc.doctype
|
| 672 |
+
if doctype:
|
| 673 |
+
return doctype.name
|
| 674 |
+
return ''
|
| 675 |
+
|
| 676 |
+
@property
|
| 677 |
+
def public_id(self):
|
| 678 |
+
doctype = self.dom_doc.doctype
|
| 679 |
+
if doctype:
|
| 680 |
+
return doctype.publicId
|
| 681 |
+
|
| 682 |
+
@property
|
| 683 |
+
def system_url(self):
|
| 684 |
+
doctype = self.dom_doc.doctype
|
| 685 |
+
if doctype:
|
| 686 |
+
return doctype.systemId
|
| 687 |
+
|
| 688 |
+
@property
|
| 689 |
+
def encoding(self):
|
| 690 |
+
return self.dom_doc.getXmlEncoding()
|
| 691 |
+
|
| 692 |
+
@property
|
| 693 |
+
def root_name(self):
|
| 694 |
+
dom_root = self.dom_doc.getDocumentElement()
|
| 695 |
+
if dom_root:
|
| 696 |
+
return dom_root.localName
|
| 697 |
+
|
| 698 |
+
@property
|
| 699 |
+
def standalone(self):
|
| 700 |
+
return self.dom_doc.getXmlStandalone()
|
| 701 |
+
|
| 702 |
+
@property
|
| 703 |
+
def xml_version(self):
|
| 704 |
+
return self.dom_doc.getXmlVersion()
|
| 705 |
+
|
| 706 |
+
@property
|
| 707 |
+
def externalDTD(self):
|
| 708 |
+
# TODO
|
| 709 |
+
return None
|
| 710 |
+
|
| 711 |
+
@property
|
| 712 |
+
def internalDTD(self):
|
| 713 |
+
# TODO
|
| 714 |
+
return None
|
| 715 |
+
|
| 716 |
+
|
| 717 |
+
class QName(object):
|
| 718 |
+
|
| 719 |
+
def __init__(self, text_or_uri_or_element, tag=None):
|
| 720 |
+
x = text_or_uri_or_element
|
| 721 |
+
if tag is not None:
|
| 722 |
+
if not isinstance(x, basestring):
|
| 723 |
+
raise ValueError('Bad namespace URI: %s' %
|
| 724 |
+
text_or_uri_or_element)
|
| 725 |
+
if not isinstance(tag, basestring):
|
| 726 |
+
raise ValueError('Bad tag: %s', tag)
|
| 727 |
+
self.namespace = x
|
| 728 |
+
self.localname = tag
|
| 729 |
+
elif isinstance(x, _Element):
|
| 730 |
+
assert tag is None
|
| 731 |
+
dom_node = x._dom_element
|
| 732 |
+
self.namespace = dom_node.namespaceURI
|
| 733 |
+
self.localname = dom_node.localName
|
| 734 |
+
elif isinstance(x, basestring):
|
| 735 |
+
self.namespace, self.localname = split_jcnotation(x)
|
| 736 |
+
else:
|
| 737 |
+
raise ValueError('Bad argument: %s' % text_or_uri_or_element)
|
| 738 |
+
|
| 739 |
+
@property
|
| 740 |
+
def text(self):
|
| 741 |
+
if self.namespace:
|
| 742 |
+
return '{%s}%s' % (self.namespace, self.localname)
|
| 743 |
+
return self.localname
|
| 744 |
+
|
| 745 |
+
|
| 746 |
+
class XPath(object):
|
| 747 |
+
|
| 748 |
+
def __init__(self, path, namespaces=None, extensions=None, regexp=True,
|
| 749 |
+
smart_strings=True):
|
| 750 |
+
if extensions:
|
| 751 |
+
raise NotImplementedError('extensions are not supported')
|
| 752 |
+
|
| 753 |
+
xpath = xpathfac.newXPath()
|
| 754 |
+
nscontext = NamespaceContextImpl(namespaces or {})
|
| 755 |
+
xpath.setNamespaceContext(nscontext)
|
| 756 |
+
self.xpathexpr = xpath.compile(path)
|
| 757 |
+
|
| 758 |
+
def __call__(self, _etree_or_element, **_variables):
|
| 759 |
+
if len(_variables):
|
| 760 |
+
raise NotImplementedError('variables are not supported')
|
| 761 |
+
|
| 762 |
+
if isinstance(_etree_or_element, _ElementTree):
|
| 763 |
+
node = _etree_or_element._dom_doc.getDocumentElement()
|
| 764 |
+
elif isinstance(_etree_or_element, _Element):
|
| 765 |
+
node = _etree_or_element._dom_element
|
| 766 |
+
else:
|
| 767 |
+
logger.error('_etree_or_element should be an instance of '
|
| 768 |
+
'_Element/_ElementTree')
|
| 769 |
+
raise ValueError()
|
| 770 |
+
|
| 771 |
+
# TODO: heuristic
|
| 772 |
+
try:
|
| 773 |
+
return self._eval_nodeset(node)
|
| 774 |
+
except XPathExpressionException, e:
|
| 775 |
+
pass
|
| 776 |
+
|
| 777 |
+
try:
|
| 778 |
+
return self._eval_node(node)
|
| 779 |
+
except XPathExpressionException, e:
|
| 780 |
+
pass
|
| 781 |
+
|
| 782 |
+
try:
|
| 783 |
+
result = self._eval_number(node)
|
| 784 |
+
if not (isinstance(result, float) and str(result) == 'nan'):
|
| 785 |
+
return result
|
| 786 |
+
except XPathExpressionException, e:
|
| 787 |
+
pass
|
| 788 |
+
|
| 789 |
+
try:
|
| 790 |
+
return self._eval_string(node)
|
| 791 |
+
except XPathExpressionException, e:
|
| 792 |
+
pass
|
| 793 |
+
|
| 794 |
+
return self._eval_boolean(node)
|
| 795 |
+
|
| 796 |
+
def _eval_nodeset(self, node):
|
| 797 |
+
result = self.xpathexpr.evaluate(node, XPathConstants.NODESET)
|
| 798 |
+
return dom_xpath_result(result)
|
| 799 |
+
|
| 800 |
+
def _eval_node(self, node):
|
| 801 |
+
result = self.xpathexpr.evaluate(node, XPathConstants.NODE)
|
| 802 |
+
return dom_xpath_result(result)
|
| 803 |
+
|
| 804 |
+
def _eval_string(self, node):
|
| 805 |
+
result = self.xpathexpr.evaluate(node, XPathConstants.STRING)
|
| 806 |
+
return dom_xpath_result(result)
|
| 807 |
+
|
| 808 |
+
def _eval_number(self, node):
|
| 809 |
+
result = self.xpathexpr.evaluate(node, XPathConstants.NUMBER)
|
| 810 |
+
return dom_xpath_result(result)
|
| 811 |
+
|
| 812 |
+
def _eval_boolean(self, node):
|
| 813 |
+
result = self.xpathexpr.evaluate(node, XPathConstants.BOOLEAN)
|
| 814 |
+
return dom_xpath_result(result)
|
| 815 |
+
|
| 816 |
+
|
| 817 |
+
@simplegeneric
|
| 818 |
+
def dom_xpath_result(result):
|
| 819 |
+
return result
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
@dom_xpath_result.register(dom.NodeList)
|
| 823 |
+
def dom_xpath_result_nodelist(nodelist):
|
| 824 |
+
resultset = []
|
| 825 |
+
for i in range(nodelist.length):
|
| 826 |
+
item = nodelist.item(i)
|
| 827 |
+
resultset.append(dom_xpath_result(item))
|
| 828 |
+
return resultset
|
| 829 |
+
|
| 830 |
+
|
| 831 |
+
@dom_xpath_result.register(dom.Element)
|
| 832 |
+
def dom_xpath_result_element(element):
|
| 833 |
+
return _Element(element)
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
@dom_xpath_result.register(dom.Attr)
|
| 837 |
+
def dom_xpath_result_attr(attr):
|
| 838 |
+
return attr.value
|
| 839 |
+
|
| 840 |
+
|
| 841 |
+
class NamespaceContextImpl(NamespaceContext):
|
| 842 |
+
|
| 843 |
+
def __init__(self, nsmap):
|
| 844 |
+
self.nsmap = nsmap
|
| 845 |
+
|
| 846 |
+
def getNamespaceURI(self, prefix):
|
| 847 |
+
# TODO: default namespace, etc., not implemented
|
| 848 |
+
if prefix is None:
|
| 849 |
+
raise IllegalArgumentException()
|
| 850 |
+
if prefix in self.nsmap:
|
| 851 |
+
return self.nsmap[prefix]
|
| 852 |
+
return XMLConstants.NULL_NS_URI
|
| 853 |
+
|
| 854 |
+
def getPrefixes(self, namespaceURI):
|
| 855 |
+
if namespaceURI is None:
|
| 856 |
+
raise IllegalArgumentException()
|
| 857 |
+
return IteratorImpl(self.iterPrefixes(namespaceURI))
|
| 858 |
+
|
| 859 |
+
def getPrefix(self, namespaceURI):
|
| 860 |
+
prefixes = self.getPrefixes(namespaceURI)
|
| 861 |
+
if prefixes is None:
|
| 862 |
+
return None
|
| 863 |
+
|
| 864 |
+
def iterPrefixes(self, namespaceURI):
|
| 865 |
+
if namespaceURI is None:
|
| 866 |
+
raise IllegalArgumentException()
|
| 867 |
+
for prefix, uri in self.nsmap.items():
|
| 868 |
+
if uri == namespaceURI:
|
| 869 |
+
yield prefix
|
| 870 |
+
|
| 871 |
+
|
| 872 |
+
class IteratorImpl(Iterator):
|
| 873 |
+
|
| 874 |
+
def __init__(self, it):
|
| 875 |
+
self.it = it
|
| 876 |
+
self._goNext()
|
| 877 |
+
|
| 878 |
+
def hasNext(self):
|
| 879 |
+
return self._hasNext
|
| 880 |
+
|
| 881 |
+
def next(self):
|
| 882 |
+
if self._hasNext:
|
| 883 |
+
try:
|
| 884 |
+
return self._nextItem
|
| 885 |
+
finally:
|
| 886 |
+
self._goNext()
|
| 887 |
+
else:
|
| 888 |
+
raise NoSuchElementException()
|
| 889 |
+
|
| 890 |
+
def _goNext(self):
|
| 891 |
+
try:
|
| 892 |
+
self._nextItem = it.next()
|
| 893 |
+
self._hasNext = True
|
| 894 |
+
except StopIteration:
|
| 895 |
+
self._nextItem = None
|
| 896 |
+
self._hasNext = False
|
| 897 |
+
|
| 898 |
+
|
| 899 |
+
class XSLT(object):
|
| 900 |
+
|
| 901 |
+
def __init__(self, xsl_input, extensions=None, regexp=True,
|
| 902 |
+
access_control=None):
|
| 903 |
+
if extensions:
|
| 904 |
+
raise NotImplementedError('extensions is not supported')
|
| 905 |
+
if access_control:
|
| 906 |
+
raise NotImplementedError('access_control is not supported')
|
| 907 |
+
|
| 908 |
+
if isinstance(xsl_input, _ElementTree):
|
| 909 |
+
xsl_tree = xsl_input
|
| 910 |
+
elif isinstance(xsl_input, _Element):
|
| 911 |
+
xsl_tree = ElementTree(xsl_input)
|
| 912 |
+
else:
|
| 913 |
+
raise ValueError(xsl_input)
|
| 914 |
+
|
| 915 |
+
self.xsl_tree = xsl_tree
|
| 916 |
+
self.xsl_source = DOMSource(xsl_tree._dom_doc, xsl_tree.docinfo.URL)
|
| 917 |
+
self.uri_resolver = __URIResolverImpl(xsl_tree)
|
| 918 |
+
|
| 919 |
+
#print tostring(xsl_tree)
|
| 920 |
+
fac = TransformerFactory.newInstance()
|
| 921 |
+
fac.setURIResolver(self.uri_resolver)
|
| 922 |
+
fac = instrument_xalan_transformer_factory(fac)
|
| 923 |
+
self.transformer = fac.newTransformer(self.xsl_source)
|
| 924 |
+
|
| 925 |
+
def __call__(self, _input, profile_run=False, **kw):
|
| 926 |
+
|
| 927 |
+
nsmap = dict(xsl='http://www.w3.org/1999/XSL/Transform')
|
| 928 |
+
output_method = 'xml'
|
| 929 |
+
output_encoding = 'utf-8'
|
| 930 |
+
|
| 931 |
+
nodes = self.xsl_tree.xpath('xsl:output/@method', namespaces=nsmap)
|
| 932 |
+
if len(nodes) > 0:
|
| 933 |
+
output_method = nodes[0]
|
| 934 |
+
|
| 935 |
+
nodes = self.xsl_tree.xpath('xsl:output/@encoding', namespaces=nsmap)
|
| 936 |
+
if len(nodes) > 0:
|
| 937 |
+
output_encoding = nodes[0]
|
| 938 |
+
|
| 939 |
+
#print tostring(_input)
|
| 940 |
+
if isinstance(_input, _ElementTree):
|
| 941 |
+
doc_source = DOMSource(_input._dom_doc)
|
| 942 |
+
elif isinstance(_input, _Element):
|
| 943 |
+
# Xalan-J 2.7.1 does not support a DOMSource from an Element
|
| 944 |
+
# so we build new document
|
| 945 |
+
dom_doc = builder.newDocument()
|
| 946 |
+
dom_root = dom_doc.importNode(_input._dom_element, True)
|
| 947 |
+
dom_doc.appendChild(dom_root)
|
| 948 |
+
doc_source = DOMSource(dom_doc)
|
| 949 |
+
else:
|
| 950 |
+
raise NotImplementedError()
|
| 951 |
+
|
| 952 |
+
if output_method in ('xml', 'html'):
|
| 953 |
+
|
| 954 |
+
# TODO: for testing
|
| 955 |
+
outputstream = ByteArrayOutputStream()
|
| 956 |
+
result = StreamResult(outputstream)
|
| 957 |
+
self.transformer.transform(doc_source, result)
|
| 958 |
+
bytes = outputstream.toByteArray()
|
| 959 |
+
inputstream = ByteArrayInputStream(bytes)
|
| 960 |
+
try:
|
| 961 |
+
dom_doc = builder.parse(inputstream)
|
| 962 |
+
except:
|
| 963 |
+
import sys
|
| 964 |
+
sys.stderr.write(bytes.tostring())
|
| 965 |
+
raise
|
| 966 |
+
result_tree = _ElementTree(dom_doc)
|
| 967 |
+
return result_tree
|
| 968 |
+
|
| 969 |
+
result = DOMResult()
|
| 970 |
+
self.transformer.transform(doc_source, result)
|
| 971 |
+
dom_doc = result.getNode()
|
| 972 |
+
result_tree = _ElementTree(dom_doc)
|
| 973 |
+
#print tostring(result_tree)
|
| 974 |
+
return result_tree
|
| 975 |
+
else:
|
| 976 |
+
outputstream = ByteArrayOutputStream()
|
| 977 |
+
result = StreamResult(outputstream)
|
| 978 |
+
self.transformer.transform(doc_source, result)
|
| 979 |
+
|
| 980 |
+
resultdoc = builder.newDocument()
|
| 981 |
+
resulttree = _XSLTResultTree(resultdoc)
|
| 982 |
+
resulttree._text = outputstream.toString(output_encoding)
|
| 983 |
+
return resulttree
|
| 984 |
+
|
| 985 |
+
|
| 986 |
+
class _XSLTResultTree(_ElementTree):
|
| 987 |
+
|
| 988 |
+
def __unicode__(self):
|
| 989 |
+
return self._text
|
| 990 |
+
|
| 991 |
+
def __str__(self):
|
| 992 |
+
return self._text.encode(self.docinfo.encoding)
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
class __URIResolverImpl(URIResolver):
|
| 996 |
+
|
| 997 |
+
def __init__(self, tree):
|
| 998 |
+
self.__logger = logging.getLogger(__name__+'.'+type(self).__name__)
|
| 999 |
+
self.base = tree.docinfo.URL
|
| 1000 |
+
|
| 1001 |
+
def resolve(self, href, base):
|
| 1002 |
+
self.__logger.debug('default_base: %r', self.base)
|
| 1003 |
+
self.__logger.debug('href: %r', href)
|
| 1004 |
+
self.__logger.debug('base: %r', base)
|
| 1005 |
+
if base is None:
|
| 1006 |
+
base = self.base
|
| 1007 |
+
|
| 1008 |
+
if base is not None:
|
| 1009 |
+
href = urljoin(base, href)
|
| 1010 |
+
else:
|
| 1011 |
+
href = href
|
| 1012 |
+
|
| 1013 |
+
href = urlparse(href)
|
| 1014 |
+
|
| 1015 |
+
# if href is not a URL
|
| 1016 |
+
if not href.scheme and not href.hostname:
|
| 1017 |
+
return StreamSource(File(href.path))
|
| 1018 |
+
|
| 1019 |
+
|
| 1020 |
+
def parse(source, parser=None, base_url=None):
|
| 1021 |
+
if base_url is not None:
|
| 1022 |
+
raise NotImplementedError('base_url is not supported')
|
| 1023 |
+
|
| 1024 |
+
if isinstance(source, basestring):
|
| 1025 |
+
with open(source) as f:
|
| 1026 |
+
return ElementTree(file=f)
|
| 1027 |
+
if hasattr(source, 'read'):
|
| 1028 |
+
return ElementTree(file=source)
|
| 1029 |
+
assert False, 'source should be a string or file-like object'
|
| 1030 |
+
|
| 1031 |
+
|
| 1032 |
+
def fromstring(text, parser=None, base_url=None):
|
| 1033 |
+
if base_url is not None:
|
| 1034 |
+
raise NotImplementedError('base_url is not supported')
|
| 1035 |
+
from StringIO import StringIO
|
| 1036 |
+
f = StringIO(text)
|
| 1037 |
+
return ElementTree(file=f)
|
| 1038 |
+
|
| 1039 |
+
|
| 1040 |
+
def tostring(element_or_tree, encoding=None, method='xml',
|
| 1041 |
+
xml_declaration=None, pretty_print=False, with_tail=True,
|
| 1042 |
+
standalone=None, doctype=None, exclusive=False,
|
| 1043 |
+
with_comments=True, inclusive_ns_prefixes=None):
|
| 1044 |
+
if isinstance(element_or_tree, _ElementTree):
|
| 1045 |
+
source = DOMSource(element_or_tree._dom_doc)
|
| 1046 |
+
else:
|
| 1047 |
+
source = DOMSource(element_or_tree._dom_element)
|
| 1048 |
+
|
| 1049 |
+
outputstream = ByteArrayOutputStream()
|
| 1050 |
+
result = StreamResult(outputstream)
|
| 1051 |
+
transformer = transformfac.newTransformer()
|
| 1052 |
+
if xml_declaration:
|
| 1053 |
+
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
|
| 1054 |
+
'no')
|
| 1055 |
+
else:
|
| 1056 |
+
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
|
| 1057 |
+
'yes')
|
| 1058 |
+
if pretty_print:
|
| 1059 |
+
transformer.setOutputProperty(OutputKeys.INDENT, 'yes')
|
| 1060 |
+
else:
|
| 1061 |
+
transformer.setOutputProperty(OutputKeys.INDENT, 'no')
|
| 1062 |
+
transformer.transform(source, result)
|
| 1063 |
+
|
| 1064 |
+
if encoding is None:
|
| 1065 |
+
encoding = 'ascii'
|
| 1066 |
+
return outputstream.toString(encoding)
|
| 1067 |
+
|
| 1068 |
+
|
| 1069 |
+
def XML(s):
|
| 1070 |
+
from StringIO import StringIO
|
| 1071 |
+
sio = StringIO(s)
|
| 1072 |
+
return parse(sio).getroot()
|
tools/jxml/lxml/lxml/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from jxml import *
|
tools/jxml/lxml/lxml/etree/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from jxml.etree import *
|
tools/jxml/lxml/setup.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from setuptools import setup, find_packages
|
| 3 |
+
import os.path
|
| 4 |
+
import sys
|
| 5 |
+
if not sys.platform.startswith('java'):
|
| 6 |
+
raise SystemExit(1)
|
| 7 |
+
os.chdir(os.path.dirname(__file__))
|
| 8 |
+
setup(name='jxml.lxml',
|
| 9 |
+
packages=find_packages())
|
tools/jxml/setup.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from setuptools import setup, find_packages
|
| 3 |
+
import os.path
|
| 4 |
+
import sys
|
| 5 |
+
if not sys.platform.startswith('java'):
|
| 6 |
+
raise SystemExit(1)
|
| 7 |
+
os.chdir(os.path.dirname(__file__))
|
| 8 |
+
setup(name='jxml',
|
| 9 |
+
packages=find_packages())
|
tools/jxml/tests/Makefile
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BUILDOUT_DIR := ${realpath ../../..}
|
| 2 |
+
JXML_DIR := ${realpath ..}
|
| 3 |
+
JXML_BIN_DIR:=$(JXML_DIR)/bin
|
| 4 |
+
JXML_LXML_DIR := ${JXML_DIR}/lxml
|
| 5 |
+
TEST_XSL_DIR := ${JXML_DIR}/tests/test-xsl
|
| 6 |
+
XML_FIXT_DIR := ${TEST_XSL_DIR}/fxt
|
| 7 |
+
TEST_GEN_DIR := ${TEST_XSL_DIR}/gen
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
XALAN_J_ARCHIVE = ${BUILDOUT_DIR}/parts/xalan-j_2_7_1-bin-2jars.tar.gz
|
| 12 |
+
XALAN_J_DIR = ${BUILDOUT_DIR}/parts/xalan-j_2_7_1
|
| 13 |
+
XALAN_J_CLASSPATH = ${XALAN_J_DIR}/xalan.jar:${XALAN_J_DIR}/xercesImpl.jar:${XALAN_J_DIR}/serializer.jar:${XALAN_J_DIR}/xml-apis.jar
|
| 14 |
+
XALAN_J_SYS_PROPS = -Djavax.xml.transform.TransformerFactory=org.apache.xalan.processor.TransformerFactoryImpl -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl
|
| 15 |
+
XALAN_J_ENVSET = CLASSPATH=${XALAN_J_CLASSPATH}
|
| 16 |
+
XALAN_J_JYTHON = ${XALAN_J_ENVSET} ${JXML_BIN_DIR}/jython ${XALAN_J_SYS_PROPS}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
DEFAULT_JYTHON = ${JXML_BIN_DIR}/jython
|
| 20 |
+
JXML_ENVSET = JYTHONPATH=${JXML_DIR}:${JXML_LXML_DIR}
|
| 21 |
+
|
| 22 |
+
.PHONY: test-jaxp test-jaxp-xalan test-python test-jython test-jython-xalan test-xsl test-xsl-xalan test-jxml-cov-test test-jxml-annotate
|
| 23 |
+
all: test-jaxp test-jaxp-xalan test-python test-jython test-jython-xalan test-xsl test-xsl-xalan test-jxml-cov-test test-jxml-annotate
|
| 24 |
+
|
| 25 |
+
test-python:
|
| 26 |
+
python test_lxml.py
|
| 27 |
+
test-jython:
|
| 28 |
+
${JXML_ENVSET} ${DEFAULT_JYTHON} test_lxml.py
|
| 29 |
+
test-jython-xalan: ${XALAN_J_DIR}
|
| 30 |
+
#${XALAN_J_JYTHON} test_textout.py
|
| 31 |
+
${JXML_ENVSET} ${XALAN_J_JYTHON} test_lxml.py
|
| 32 |
+
test-xsl:
|
| 33 |
+
for fn in `ls ${BUILDOUT_DIR}/test-xsl/gen/*.py`; do echo $$fn; ${JXML_ENVSET} ${DEFAULT_JYTHON} $$fn; done
|
| 34 |
+
test-xsl-xalan: ${XALAN_J_DIR}
|
| 35 |
+
for fn in `ls ${BUILDOUT_DIR}/test-xsl/gen/*.py`; do echo $$fn; ${JXML_ENVSET} ${XALAN_J_JYTHON} $$fn; done
|
| 36 |
+
test-jaxp:
|
| 37 |
+
${DEFAULT_JYTHON} test_jaxp.py
|
| 38 |
+
test-jaxp-xalan:
|
| 39 |
+
${XALAN_J_JYTHON} test_jaxp.py
|
| 40 |
+
|
| 41 |
+
test-jxml-cov-test:
|
| 42 |
+
${XALAN_J_ENVSET} ${JXML_BIN_DIR}/jxml-cov-test coverage.xml ${BUILDOUT_DIR}/test-xsl/gen/*.py
|
| 43 |
+
test-jxml-annotate: test-jxml-cov-test
|
| 44 |
+
${JXML_BIN_DIR}/jxml-annotate --color=yes coverage.xml > coverage.color.txt
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
${XALAN_J_DIR}: ${XALAN_J_ARCHIVE}
|
| 49 |
+
${BUILDOUT_DIR}/bin/pyhwp-unpack $< $@
|
| 50 |
+
|
| 51 |
+
${XALAN_J_ARCHIVE}:
|
| 52 |
+
${BUILDOUT_DIR}/bin/pyhwp-download http://mirror.apache-kr.org/xalan/xalan-j/binaries/xalan-j_2_7_1-bin-2jars.tar.gz ${BUILDOUT_DIR}/parts 3ccda39bcd08b780436dfd2f22fb23d5
|
tools/jxml/tests/hello.xml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?><hello />
|
tools/jxml/tests/sample.xml
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<x:document xmlns:x="http://example.tld" version="1.0">
|
| 3 |
+
<x:paragraph>text<x:span>foo</x:span></x:paragraph>tail<z:object xmlns:z="http://z.example.tld" a="1" x:b="2" z:c="3" />
|
| 4 |
+
<x:third />
|
| 5 |
+
</x:document>
|
tools/jxml/tests/test_jaxp.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
|
| 3 |
+
import unittest
|
| 4 |
+
|
| 5 |
+
from java.lang import System
|
| 6 |
+
from java.io import File
|
| 7 |
+
from java.io import ByteArrayOutputStream
|
| 8 |
+
from javax.xml.parsers import DocumentBuilderFactory
|
| 9 |
+
from javax.xml.transform import TransformerFactory
|
| 10 |
+
from javax.xml.transform.dom import DOMSource
|
| 11 |
+
from javax.xml.transform.stream import StreamSource
|
| 12 |
+
from javax.xml.transform.stream import StreamResult
|
| 13 |
+
|
| 14 |
+
dbfac = DocumentBuilderFactory.newInstance()
|
| 15 |
+
dbfac.namespaceAware = True
|
| 16 |
+
docfac = dbfac.newDocumentBuilder()
|
| 17 |
+
print type(dbfac)
|
| 18 |
+
|
| 19 |
+
transfac = TransformerFactory.newInstance()
|
| 20 |
+
|
| 21 |
+
src_dom = docfac.parse('hello.xml')
|
| 22 |
+
src_source = DOMSource(src_dom)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def unsigned_byte(x):
|
| 26 |
+
if x < 0:
|
| 27 |
+
return 256 + x
|
| 28 |
+
return x
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def Transformer(xsl_source):
|
| 32 |
+
transformer = transfac.newTransformer(xsl_source)
|
| 33 |
+
def transform(src_source):
|
| 34 |
+
outputstream = ByteArrayOutputStream()
|
| 35 |
+
dst_result = StreamResult(outputstream)
|
| 36 |
+
transformer.transform(src_source, dst_result)
|
| 37 |
+
return ''.join(chr(unsigned_byte(x)) for x in outputstream.toByteArray())
|
| 38 |
+
return transform
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def transform(xsl_source, src_source):
|
| 42 |
+
transform = Transformer(xsl_source)
|
| 43 |
+
return transform(src_source)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class TestXSLT(unittest.TestCase):
|
| 47 |
+
|
| 48 |
+
xsl_path = 'xsl/import-test.xsl'
|
| 49 |
+
|
| 50 |
+
def test_xsl_dom(self):
|
| 51 |
+
|
| 52 |
+
xsl_dom = docfac.parse(self.xsl_path)
|
| 53 |
+
# DOMSource with System Id
|
| 54 |
+
xsl_source = DOMSource(xsl_dom, self.xsl_path)
|
| 55 |
+
|
| 56 |
+
result = transform(xsl_source, src_source)
|
| 57 |
+
#print result
|
| 58 |
+
self.assertTrue('world' in result)
|
| 59 |
+
|
| 60 |
+
def test_xsl_stream(self):
|
| 61 |
+
xsl_source = StreamSource(self.xsl_path)
|
| 62 |
+
|
| 63 |
+
result = transform(xsl_source, src_source)
|
| 64 |
+
#print result
|
| 65 |
+
self.assertTrue('world' in result)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
if __name__ == '__main__':
|
| 69 |
+
unittest.main()
|
tools/jxml/tests/test_lxml.py
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from __future__ import with_statement
|
| 3 |
+
|
| 4 |
+
import os.path
|
| 5 |
+
import sys
|
| 6 |
+
import unittest
|
| 7 |
+
from unittest import TestCase
|
| 8 |
+
|
| 9 |
+
from lxml import etree
|
| 10 |
+
from lxml.etree import ElementTree
|
| 11 |
+
from lxml.etree import Element
|
| 12 |
+
from lxml.etree import SubElement
|
| 13 |
+
from lxml.etree import QName
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class ElementTreeTest(TestCase):
|
| 17 |
+
|
| 18 |
+
def test_etree_parse(self):
|
| 19 |
+
with open('sample.xml') as f:
|
| 20 |
+
et = etree.parse(f)
|
| 21 |
+
et = etree.parse('sample.xml')
|
| 22 |
+
|
| 23 |
+
def test_etree_fromstring(self):
|
| 24 |
+
with open('sample.xml') as f:
|
| 25 |
+
text = f.read()
|
| 26 |
+
et = etree.fromstring(text)
|
| 27 |
+
|
| 28 |
+
def test_etree_from_file(self):
|
| 29 |
+
with open('sample.xml') as f:
|
| 30 |
+
et = ElementTree(file=f)
|
| 31 |
+
root = et.getroot()
|
| 32 |
+
self.assertEqual('{http://example.tld}document', root.tag)
|
| 33 |
+
self.assertEqual('x', root.prefix)
|
| 34 |
+
self.assertTrue('x' in root.nsmap)
|
| 35 |
+
|
| 36 |
+
with open('hello.xml') as f:
|
| 37 |
+
et = ElementTree(file=f)
|
| 38 |
+
root = et.getroot()
|
| 39 |
+
self.assertEqual('hello', root.tag)
|
| 40 |
+
self.assertEqual(None, root.prefix)
|
| 41 |
+
self.assertEqual({}, root.nsmap)
|
| 42 |
+
|
| 43 |
+
def test_etree_tostring(self):
|
| 44 |
+
with open('sample.xml') as f:
|
| 45 |
+
et = etree.parse(f)
|
| 46 |
+
etree.tostring(et, encoding='utf-8', xml_declaration=True)
|
| 47 |
+
etree.tostring(et.getroot()[0], encoding='utf-8', xml_declaration=True)
|
| 48 |
+
|
| 49 |
+
def test_from_element(self):
|
| 50 |
+
elem = Element('document')
|
| 51 |
+
tree = ElementTree(elem)
|
| 52 |
+
self.assertEqual('document', tree.getroot().tag)
|
| 53 |
+
|
| 54 |
+
with open('sample.xml') as f:
|
| 55 |
+
et = ElementTree(file=f)
|
| 56 |
+
root = et.getroot()
|
| 57 |
+
|
| 58 |
+
tree = ElementTree(root)
|
| 59 |
+
self.assertEqual(root.base, tree.getroot().base)
|
| 60 |
+
self.assertEqual(et.docinfo.URL, tree.docinfo.URL)
|
| 61 |
+
|
| 62 |
+
def test_docinfo(self):
|
| 63 |
+
with open('sample.xml') as f:
|
| 64 |
+
et = etree.parse(f)
|
| 65 |
+
import os.path
|
| 66 |
+
self.assertEqual(os.path.abspath('sample.xml'), et.docinfo.URL)
|
| 67 |
+
self.assertEqual('', et.docinfo.doctype)
|
| 68 |
+
self.assertEqual('utf-8', et.docinfo.encoding)
|
| 69 |
+
self.assertEqual(None, et.docinfo.externalDTD)
|
| 70 |
+
self.assertEqual(None, et.docinfo.internalDTD)
|
| 71 |
+
self.assertEqual(None, et.docinfo.public_id)
|
| 72 |
+
self.assertEqual('document', et.docinfo.root_name)
|
| 73 |
+
self.assertFalse(et.docinfo.standalone)
|
| 74 |
+
self.assertEqual(None, et.docinfo.system_url)
|
| 75 |
+
self.assertEqual('1.0', et.docinfo.xml_version)
|
| 76 |
+
|
| 77 |
+
et.docinfo.URL = 'http://example.tld'
|
| 78 |
+
self.assertEqual('http://example.tld', et.docinfo.URL)
|
| 79 |
+
|
| 80 |
+
def test_parser(self):
|
| 81 |
+
pass
|
| 82 |
+
|
| 83 |
+
def test__copy__(self):
|
| 84 |
+
pass
|
| 85 |
+
|
| 86 |
+
def test__deepcopy__(self):
|
| 87 |
+
pass
|
| 88 |
+
|
| 89 |
+
def test_setroot(self):
|
| 90 |
+
from lxml.etree import XML
|
| 91 |
+
a = XML('<a />').getroottree()
|
| 92 |
+
b = XML('<b />').getroottree()
|
| 93 |
+
a._setroot(b.getroot())
|
| 94 |
+
self.assertEqual('b', a.getroot().tag)
|
| 95 |
+
|
| 96 |
+
def test_find(self):
|
| 97 |
+
pass
|
| 98 |
+
|
| 99 |
+
def test_findall(self):
|
| 100 |
+
pass
|
| 101 |
+
|
| 102 |
+
def test_findtext(self):
|
| 103 |
+
pass
|
| 104 |
+
|
| 105 |
+
def test_getiterator(self):
|
| 106 |
+
pass
|
| 107 |
+
|
| 108 |
+
def test_getpath(self):
|
| 109 |
+
pass
|
| 110 |
+
|
| 111 |
+
def test_getroot(self):
|
| 112 |
+
with open('sample.xml') as f:
|
| 113 |
+
et = etree.parse(f)
|
| 114 |
+
tree = etree.parse('sample.xml')
|
| 115 |
+
root = tree.getroot()
|
| 116 |
+
self.assertEqual('{http://example.tld}document', root.tag)
|
| 117 |
+
|
| 118 |
+
def test_iter(self):
|
| 119 |
+
pass
|
| 120 |
+
|
| 121 |
+
def test_iterfind(self):
|
| 122 |
+
pass
|
| 123 |
+
|
| 124 |
+
def test_parse(self):
|
| 125 |
+
pass
|
| 126 |
+
|
| 127 |
+
def test_relaxng(self):
|
| 128 |
+
pass
|
| 129 |
+
|
| 130 |
+
def test_write(self):
|
| 131 |
+
with open('sample.xml') as f:
|
| 132 |
+
tree = etree.parse(f)
|
| 133 |
+
|
| 134 |
+
import tempfile
|
| 135 |
+
import os
|
| 136 |
+
fd, name = tempfile.mkstemp()
|
| 137 |
+
try:
|
| 138 |
+
with os.fdopen(fd, 'w') as f:
|
| 139 |
+
tree.write(f)
|
| 140 |
+
with open(name) as f:
|
| 141 |
+
tree2 = etree.parse(f)
|
| 142 |
+
finally:
|
| 143 |
+
os.unlink(name)
|
| 144 |
+
self.assertEqual(tree.getroot().tag, tree2.getroot().tag)
|
| 145 |
+
|
| 146 |
+
def test_write_c14n(self):
|
| 147 |
+
pass
|
| 148 |
+
|
| 149 |
+
def test_xinclude(self):
|
| 150 |
+
pass
|
| 151 |
+
|
| 152 |
+
def test_xmlschema(self):
|
| 153 |
+
pass
|
| 154 |
+
|
| 155 |
+
def test_xpath(self):
|
| 156 |
+
with open('sample.xml') as f:
|
| 157 |
+
et = etree.parse(f)
|
| 158 |
+
tree = etree.parse('sample.xml')
|
| 159 |
+
|
| 160 |
+
nsmap = dict(x='http://example.tld')
|
| 161 |
+
|
| 162 |
+
# element
|
| 163 |
+
result = tree.xpath('//x:paragraph', namespaces=nsmap)
|
| 164 |
+
self.assertEqual(1, len(result))
|
| 165 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 166 |
+
result[0].tag)
|
| 167 |
+
|
| 168 |
+
# attribute
|
| 169 |
+
result = tree.xpath('@version', namespaces=nsmap)
|
| 170 |
+
self.assertEqual(['1.0'], result)
|
| 171 |
+
|
| 172 |
+
# string
|
| 173 |
+
result = tree.xpath('"foo"', namespaces=nsmap)
|
| 174 |
+
self.assertEqual('foo', result)
|
| 175 |
+
|
| 176 |
+
# number expression
|
| 177 |
+
result = tree.xpath('1', namespaces=nsmap)
|
| 178 |
+
self.assertEqual(1.0, result)
|
| 179 |
+
|
| 180 |
+
result = tree.xpath('"1.0"', namespaces=nsmap)
|
| 181 |
+
# should be string, but alas, it returns a number in jxml
|
| 182 |
+
if sys.platform.startswith('java'):
|
| 183 |
+
self.assertEqual(1.0, result)
|
| 184 |
+
else:
|
| 185 |
+
self.assertEqual('1.0', result)
|
| 186 |
+
|
| 187 |
+
def test_xslt(self):
|
| 188 |
+
tree = etree.XML('<hello />').getroottree()
|
| 189 |
+
with open('xsl/import-test.xsl') as f:
|
| 190 |
+
xsl_tree = etree.parse(f)
|
| 191 |
+
result = tree.xslt(xsl_tree)
|
| 192 |
+
self.assertEqual('world', result.getroot().tag)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
class ElementTest(TestCase):
|
| 196 |
+
|
| 197 |
+
def setUp(self):
|
| 198 |
+
with open('sample.xml') as f:
|
| 199 |
+
self.et = ElementTree(file=f)
|
| 200 |
+
self.root = self.et.getroot()
|
| 201 |
+
|
| 202 |
+
def tearDown(self):
|
| 203 |
+
pass
|
| 204 |
+
|
| 205 |
+
def test_Element(self):
|
| 206 |
+
elem = Element('document', dict(a='1', b='2'), c='3')
|
| 207 |
+
self.assertEqual('document', elem.tag)
|
| 208 |
+
self.assertEqual(dict(a='1', b='2', c='3'),
|
| 209 |
+
elem.attrib)
|
| 210 |
+
|
| 211 |
+
nsmap = dict(a='http://a.example.tld', c='http://c.example.tld')
|
| 212 |
+
elem = Element('{http://a.example.tld}document',
|
| 213 |
+
{'{http://a.example.tld}a': '1',
|
| 214 |
+
'{http://b.example.tld}a': '2',
|
| 215 |
+
'a': '3'},
|
| 216 |
+
nsmap)
|
| 217 |
+
|
| 218 |
+
self.assertEqual('a', elem.prefix)
|
| 219 |
+
self.assertEqual('{http://a.example.tld}document', elem.tag)
|
| 220 |
+
self.assertEqual(nsmap['a'], elem.nsmap['a'])
|
| 221 |
+
self.assertEqual(nsmap['c'], elem.nsmap['c'])
|
| 222 |
+
self.assertTrue('http://b.example.tld' in elem.nsmap.values())
|
| 223 |
+
self.assertEqual('1', elem.get('{http://a.example.tld}a'))
|
| 224 |
+
self.assertEqual('2', elem.get('{http://b.example.tld}a'))
|
| 225 |
+
self.assertEqual('3', elem.get('a'))
|
| 226 |
+
|
| 227 |
+
def test_SubElement(self):
|
| 228 |
+
elem = Element('document')
|
| 229 |
+
child = SubElement(elem, 'paragraph')
|
| 230 |
+
grandchild = SubElement(child, 'span')
|
| 231 |
+
self.assertEqual('paragraph', elem[0].tag)
|
| 232 |
+
self.assertEqual('span', elem[0][0].tag)
|
| 233 |
+
|
| 234 |
+
def test_base(self):
|
| 235 |
+
uri = os.path.abspath('sample.xml')
|
| 236 |
+
with open(uri) as f:
|
| 237 |
+
et = etree.parse(f)
|
| 238 |
+
root = et.getroot()
|
| 239 |
+
self.assertEqual(uri, root.base)
|
| 240 |
+
|
| 241 |
+
def test_tag(self):
|
| 242 |
+
elem = Element('HwpDoc')
|
| 243 |
+
self.assertEqual('HwpDoc', elem.tag)
|
| 244 |
+
|
| 245 |
+
elem = Element('{http://www.w3.org/1999/XSL/Transform}stylesheet')
|
| 246 |
+
self.assertTrue(elem.prefix)
|
| 247 |
+
self.assertEqual('{http://www.w3.org/1999/XSL/Transform}stylesheet', elem.tag)
|
| 248 |
+
|
| 249 |
+
elem = Element('{http://www.w3.org/1999/XSL/Transform}stylesheet',
|
| 250 |
+
nsmap=dict(xsl='http://www.w3.org/1999/XSL/Transform'))
|
| 251 |
+
self.assertEqual('xsl', elem.prefix)
|
| 252 |
+
self.assertEqual('{http://www.w3.org/1999/XSL/Transform}stylesheet', elem.tag)
|
| 253 |
+
|
| 254 |
+
def test_nsmap(self):
|
| 255 |
+
self.assertEqual(dict(x='http://example.tld'),
|
| 256 |
+
self.root.nsmap)
|
| 257 |
+
self.assertEqual(dict(x='http://example.tld',
|
| 258 |
+
z='http://z.example.tld'),
|
| 259 |
+
self.root[1].nsmap)
|
| 260 |
+
|
| 261 |
+
def test_prefix(self):
|
| 262 |
+
self.assertEqual('x', self.root.prefix)
|
| 263 |
+
self.assertEqual('z', self.root[1].prefix)
|
| 264 |
+
|
| 265 |
+
def test_text(self):
|
| 266 |
+
self.assertEqual('text', self.root[0].text)
|
| 267 |
+
self.assertEqual(None, self.root[1].text)
|
| 268 |
+
|
| 269 |
+
def test_tail(self):
|
| 270 |
+
self.assertEqual('tail', self.root[0].tail)
|
| 271 |
+
self.assertEqual(None, self.root[0][0].tail)
|
| 272 |
+
|
| 273 |
+
def test_attrib(self):
|
| 274 |
+
self.assertEqual({'version': '1.0'},
|
| 275 |
+
self.root.attrib)
|
| 276 |
+
self.assertEqual({'a': '1',
|
| 277 |
+
'{http://example.tld}b': '2',
|
| 278 |
+
'{http://z.example.tld}c': '3'},
|
| 279 |
+
self.root[1].attrib)
|
| 280 |
+
|
| 281 |
+
def test__contains__(self):
|
| 282 |
+
pass
|
| 283 |
+
|
| 284 |
+
def test__copy__(self):
|
| 285 |
+
pass
|
| 286 |
+
|
| 287 |
+
def test__deepcopy__(self):
|
| 288 |
+
pass
|
| 289 |
+
|
| 290 |
+
def test__delitem__(self):
|
| 291 |
+
pass
|
| 292 |
+
|
| 293 |
+
def test__getitem__(self):
|
| 294 |
+
paragraph = self.root.__getitem__(0)
|
| 295 |
+
self.assertEqual('{http://example.tld}paragraph', paragraph.tag)
|
| 296 |
+
paragraph = self.root[0]
|
| 297 |
+
self.assertEqual('{http://example.tld}paragraph', paragraph.tag)
|
| 298 |
+
|
| 299 |
+
paragraph = self.root.__getitem__(1)
|
| 300 |
+
self.assertEqual('{http://z.example.tld}object', paragraph.tag)
|
| 301 |
+
paragraph = self.root[1]
|
| 302 |
+
self.assertEqual('{http://z.example.tld}object', paragraph.tag)
|
| 303 |
+
|
| 304 |
+
child = self.root.__getitem__(-1)
|
| 305 |
+
self.assertEqual('{http://example.tld}third', child.tag)
|
| 306 |
+
child = self.root[-1]
|
| 307 |
+
self.assertEqual('{http://example.tld}third', child.tag)
|
| 308 |
+
|
| 309 |
+
def test__iter__(self):
|
| 310 |
+
it = self.root.__iter__()
|
| 311 |
+
|
| 312 |
+
paragraph = it.next()
|
| 313 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 314 |
+
paragraph.tag)
|
| 315 |
+
self.assertEqual('text', paragraph.text)
|
| 316 |
+
self.assertEqual('tail', paragraph.tail)
|
| 317 |
+
|
| 318 |
+
paragraph = it.next()
|
| 319 |
+
self.assertEqual('{http://z.example.tld}object',
|
| 320 |
+
paragraph.tag)
|
| 321 |
+
|
| 322 |
+
child = it.next()
|
| 323 |
+
self.assertEqual('{http://example.tld}third',
|
| 324 |
+
child.tag)
|
| 325 |
+
|
| 326 |
+
self.assertRaises(StopIteration, it.next)
|
| 327 |
+
|
| 328 |
+
def test__len__(self):
|
| 329 |
+
self.assertEqual(3, self.root.__len__())
|
| 330 |
+
self.assertEqual(3, len(self.root))
|
| 331 |
+
|
| 332 |
+
def test__nonzero__(self):
|
| 333 |
+
pass
|
| 334 |
+
|
| 335 |
+
def test__repr__(self):
|
| 336 |
+
pass
|
| 337 |
+
|
| 338 |
+
def test__reversed__(self):
|
| 339 |
+
it = self.root.__reversed__()
|
| 340 |
+
|
| 341 |
+
child = it.next()
|
| 342 |
+
self.assertEqual('{http://example.tld}third',
|
| 343 |
+
child.tag)
|
| 344 |
+
|
| 345 |
+
paragraph = it.next()
|
| 346 |
+
self.assertEqual('{http://z.example.tld}object',
|
| 347 |
+
paragraph.tag)
|
| 348 |
+
|
| 349 |
+
paragraph = it.next()
|
| 350 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 351 |
+
paragraph.tag)
|
| 352 |
+
self.assertEqual('text', paragraph.text)
|
| 353 |
+
self.assertEqual('tail', paragraph.tail)
|
| 354 |
+
|
| 355 |
+
self.assertRaises(StopIteration, it.next)
|
| 356 |
+
|
| 357 |
+
def test__setitem__(self):
|
| 358 |
+
new_child = Element('new-child')
|
| 359 |
+
self.root.__setitem__(1, new_child)
|
| 360 |
+
self.assertEqual('new-child', self.root[1].tag)
|
| 361 |
+
|
| 362 |
+
new_child = Element('new-child2')
|
| 363 |
+
self.assertRaises(IndexError, self.root.__setitem__, 3, new_child)
|
| 364 |
+
|
| 365 |
+
def test_addnext(self):
|
| 366 |
+
pass
|
| 367 |
+
|
| 368 |
+
def test_addprevious(self):
|
| 369 |
+
pass
|
| 370 |
+
|
| 371 |
+
def test_append(self):
|
| 372 |
+
new_child = Element('new-child')
|
| 373 |
+
self.root.append(new_child)
|
| 374 |
+
|
| 375 |
+
child = self.root[3]
|
| 376 |
+
self.assertEqual('new-child', child.tag)
|
| 377 |
+
|
| 378 |
+
def test_clear(self):
|
| 379 |
+
pass
|
| 380 |
+
|
| 381 |
+
def test_extend(self):
|
| 382 |
+
pass
|
| 383 |
+
|
| 384 |
+
def test_find(self):
|
| 385 |
+
pass
|
| 386 |
+
|
| 387 |
+
def test_findall(self):
|
| 388 |
+
pass
|
| 389 |
+
|
| 390 |
+
def test_findtext(self):
|
| 391 |
+
pass
|
| 392 |
+
|
| 393 |
+
def test_get(self):
|
| 394 |
+
self.assertEqual(None, self.root.get('nonexists'))
|
| 395 |
+
self.assertEqual('1.0', self.root.get('version'))
|
| 396 |
+
self.assertEqual('1', self.root[1].get('a'))
|
| 397 |
+
self.assertEqual('2', self.root[1].get('{http://example.tld}b'))
|
| 398 |
+
self.assertEqual('3', self.root[1].get('{http://z.example.tld}c'))
|
| 399 |
+
|
| 400 |
+
def test_getchildren(self):
|
| 401 |
+
children = self.root.getchildren()
|
| 402 |
+
|
| 403 |
+
child = children[0]
|
| 404 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 405 |
+
child.tag)
|
| 406 |
+
self.assertEqual('text', child.text)
|
| 407 |
+
self.assertEqual('tail', child.tail)
|
| 408 |
+
|
| 409 |
+
child = children[1]
|
| 410 |
+
self.assertEqual('{http://z.example.tld}object',
|
| 411 |
+
child.tag)
|
| 412 |
+
|
| 413 |
+
child = children[2]
|
| 414 |
+
self.assertEqual('{http://example.tld}third',
|
| 415 |
+
child.tag)
|
| 416 |
+
|
| 417 |
+
def test_getiterator(self):
|
| 418 |
+
pass
|
| 419 |
+
|
| 420 |
+
def test_getnext(self):
|
| 421 |
+
child = self.root[0]
|
| 422 |
+
child = child.getnext()
|
| 423 |
+
self.assertEqual('{http://z.example.tld}object',
|
| 424 |
+
child.tag)
|
| 425 |
+
child = child.getnext()
|
| 426 |
+
self.assertEqual('{http://example.tld}third',
|
| 427 |
+
child.tag)
|
| 428 |
+
child = child.getnext()
|
| 429 |
+
self.assertEqual(None, child)
|
| 430 |
+
|
| 431 |
+
def test_getparent(self):
|
| 432 |
+
parent = self.root.getparent()
|
| 433 |
+
self.assertEqual(None, parent)
|
| 434 |
+
|
| 435 |
+
for child in self.root:
|
| 436 |
+
parent = child.getparent()
|
| 437 |
+
self.assertEqual('{http://example.tld}document',
|
| 438 |
+
parent.tag)
|
| 439 |
+
|
| 440 |
+
def test_getprevious(self):
|
| 441 |
+
child = self.root[-1]
|
| 442 |
+
self.assertEqual('{http://example.tld}third',
|
| 443 |
+
child.tag)
|
| 444 |
+
child = child.getprevious()
|
| 445 |
+
self.assertEqual('{http://z.example.tld}object',
|
| 446 |
+
child.tag)
|
| 447 |
+
child = child.getprevious()
|
| 448 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 449 |
+
child.tag)
|
| 450 |
+
child = child.getprevious()
|
| 451 |
+
self.assertEqual(None, child)
|
| 452 |
+
|
| 453 |
+
def test_getroottree(self):
|
| 454 |
+
elem = Element('HwpDoc')
|
| 455 |
+
self.assertTrue(elem.getroottree() is not None)
|
| 456 |
+
|
| 457 |
+
def test_index(self):
|
| 458 |
+
pass
|
| 459 |
+
|
| 460 |
+
def test_insert(self):
|
| 461 |
+
pass
|
| 462 |
+
|
| 463 |
+
def test_items(self):
|
| 464 |
+
pass
|
| 465 |
+
|
| 466 |
+
def test_iter(self):
|
| 467 |
+
pass
|
| 468 |
+
|
| 469 |
+
def test_iterancestors(self):
|
| 470 |
+
span = self.root[0][0]
|
| 471 |
+
it = span.iterancestors()
|
| 472 |
+
|
| 473 |
+
parent = it.next()
|
| 474 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 475 |
+
parent.tag)
|
| 476 |
+
|
| 477 |
+
parent = it.next()
|
| 478 |
+
self.assertEqual('{http://example.tld}document',
|
| 479 |
+
parent.tag)
|
| 480 |
+
|
| 481 |
+
self.assertRaises(StopIteration, it.next)
|
| 482 |
+
|
| 483 |
+
# with tags predicate
|
| 484 |
+
|
| 485 |
+
it = span.iterancestors('{http://example.tld}document')
|
| 486 |
+
|
| 487 |
+
parent = it.next()
|
| 488 |
+
self.assertEqual('{http://example.tld}document',
|
| 489 |
+
parent.tag)
|
| 490 |
+
|
| 491 |
+
self.assertRaises(StopIteration, it.next)
|
| 492 |
+
|
| 493 |
+
def test_iterchildren(self):
|
| 494 |
+
pass
|
| 495 |
+
|
| 496 |
+
def test_descendants(self):
|
| 497 |
+
pass
|
| 498 |
+
|
| 499 |
+
def test_iterfind(self):
|
| 500 |
+
pass
|
| 501 |
+
|
| 502 |
+
def test_siblings(self):
|
| 503 |
+
pass
|
| 504 |
+
|
| 505 |
+
def test_itertext(self):
|
| 506 |
+
pass
|
| 507 |
+
|
| 508 |
+
def test_keys(self):
|
| 509 |
+
self.assertEqual(set(['version']),
|
| 510 |
+
set(self.root.keys()))
|
| 511 |
+
self.assertEqual(set(['a', '{http://example.tld}b',
|
| 512 |
+
'{http://z.example.tld}c']),
|
| 513 |
+
set(self.root[1].keys()))
|
| 514 |
+
|
| 515 |
+
def test_makeelement(self):
|
| 516 |
+
pass
|
| 517 |
+
|
| 518 |
+
def test_remove(self):
|
| 519 |
+
pass
|
| 520 |
+
|
| 521 |
+
def test_replace(self):
|
| 522 |
+
pass
|
| 523 |
+
|
| 524 |
+
def test_set(self):
|
| 525 |
+
self.root.set('{http://example.tld}a', '1')
|
| 526 |
+
self.assertEqual('1', self.root.get('{http://example.tld}a'))
|
| 527 |
+
self.root.set('{http://c.example.tld}a', '2')
|
| 528 |
+
self.assertEqual('2', self.root.get('{http://c.example.tld}a'))
|
| 529 |
+
self.root.set('a', '3')
|
| 530 |
+
self.assertEqual('3', self.root.get('a'))
|
| 531 |
+
|
| 532 |
+
def test_values(self):
|
| 533 |
+
self.root[1].values()
|
| 534 |
+
|
| 535 |
+
def test_xpath(self):
|
| 536 |
+
nsmap = dict(x='http://example.tld')
|
| 537 |
+
|
| 538 |
+
# element
|
| 539 |
+
result = self.root.xpath('//x:paragraph', namespaces=nsmap)
|
| 540 |
+
self.assertEqual(1, len(result))
|
| 541 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 542 |
+
result[0].tag)
|
| 543 |
+
|
| 544 |
+
# attribute
|
| 545 |
+
result = self.root.xpath('@version', namespaces=nsmap)
|
| 546 |
+
self.assertEqual(['1.0'], result)
|
| 547 |
+
|
| 548 |
+
# string
|
| 549 |
+
result = self.root.xpath('"foo"', namespaces=nsmap)
|
| 550 |
+
self.assertEqual('foo', result)
|
| 551 |
+
|
| 552 |
+
# number expression
|
| 553 |
+
result = self.root.xpath('1', namespaces=nsmap)
|
| 554 |
+
self.assertEqual(1.0, result)
|
| 555 |
+
|
| 556 |
+
result = self.root.xpath('"1.0"', namespaces=nsmap)
|
| 557 |
+
# should be string, but alas, it returns a number in jxml
|
| 558 |
+
if sys.platform.startswith('java'):
|
| 559 |
+
self.assertEqual(1.0, result)
|
| 560 |
+
else:
|
| 561 |
+
self.assertEqual('1.0', result)
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
class QNameTest(TestCase):
|
| 565 |
+
|
| 566 |
+
text = '{http://example.tld}document'
|
| 567 |
+
namespace = 'http://example.tld'
|
| 568 |
+
localname = 'document'
|
| 569 |
+
|
| 570 |
+
def test_from_text(self):
|
| 571 |
+
qname = QName(self.text)
|
| 572 |
+
self.assertEqual(self.text, qname.text)
|
| 573 |
+
self.assertEqual(self.namespace, qname.namespace)
|
| 574 |
+
self.assertEqual(self.localname, qname.localname)
|
| 575 |
+
|
| 576 |
+
qname = QName('document')
|
| 577 |
+
self.assertEqual('document', qname.text)
|
| 578 |
+
self.assertEqual(None, qname.namespace)
|
| 579 |
+
self.assertEqual('document', qname.localname)
|
| 580 |
+
|
| 581 |
+
def test_from_nsuri_and_tag(self):
|
| 582 |
+
qname = QName(self.namespace, self.localname)
|
| 583 |
+
self.assertEqual(self.text, qname.text)
|
| 584 |
+
self.assertEqual(self.namespace, qname.namespace)
|
| 585 |
+
self.assertEqual(self.localname, qname.localname)
|
| 586 |
+
|
| 587 |
+
def test_from_element(self):
|
| 588 |
+
element = Element(self.text)
|
| 589 |
+
qname = QName(element)
|
| 590 |
+
self.assertEqual(self.text, qname.text)
|
| 591 |
+
self.assertEqual(self.namespace, qname.namespace)
|
| 592 |
+
self.assertEqual(self.localname, qname.localname)
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
class XPathTest(TestCase):
|
| 596 |
+
|
| 597 |
+
def test_xpath(self):
|
| 598 |
+
from lxml.etree import parse
|
| 599 |
+
from lxml.etree import XPath
|
| 600 |
+
with file('sample.xml') as f:
|
| 601 |
+
doc = parse(f)
|
| 602 |
+
|
| 603 |
+
nsmap = dict(x='http://example.tld')
|
| 604 |
+
|
| 605 |
+
# element
|
| 606 |
+
xpath = XPath('//x:paragraph', namespaces=nsmap)
|
| 607 |
+
result = xpath(doc)
|
| 608 |
+
self.assertEqual(1, len(result))
|
| 609 |
+
self.assertEqual('{http://example.tld}paragraph',
|
| 610 |
+
result[0].tag)
|
| 611 |
+
|
| 612 |
+
# attribute
|
| 613 |
+
xpath = XPath('@version', namespaces=nsmap)
|
| 614 |
+
result = xpath(doc)
|
| 615 |
+
self.assertEqual(['1.0'], result)
|
| 616 |
+
|
| 617 |
+
# string
|
| 618 |
+
xpath = XPath('"foo"', namespaces=nsmap)
|
| 619 |
+
result = xpath(doc)
|
| 620 |
+
self.assertEqual('foo', result)
|
| 621 |
+
|
| 622 |
+
# number
|
| 623 |
+
xpath = XPath('1', namespaces=nsmap)
|
| 624 |
+
self.assertEqual(1, xpath(doc))
|
| 625 |
+
|
| 626 |
+
# string, but alas, it returns a number in jxml
|
| 627 |
+
xpath = XPath('"1.0"', namespaces=nsmap)
|
| 628 |
+
result = xpath(doc)
|
| 629 |
+
if sys.platform.startswith('java'):
|
| 630 |
+
self.assertEqual(1.0, result)
|
| 631 |
+
else:
|
| 632 |
+
self.assertEqual('1.0', result)
|
| 633 |
+
|
| 634 |
+
# Boolean
|
| 635 |
+
xpath = XPath('1 = 1', namespaces=nsmap)
|
| 636 |
+
self.assertEqual(True, xpath(doc))
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
class XSLTTest(TestCase):
|
| 640 |
+
|
| 641 |
+
def test_from_element(self):
|
| 642 |
+
with open('xsl/import-test.xsl') as f:
|
| 643 |
+
xsl_tree = etree.parse(f)
|
| 644 |
+
etree.XSLT(xsl_tree.getroot())
|
| 645 |
+
|
| 646 |
+
def test_xslt_with_default_parser(self):
|
| 647 |
+
with open('xsl/import-test.xsl') as f:
|
| 648 |
+
xsl_tree = etree.parse(f)
|
| 649 |
+
transform = etree.XSLT(xsl_tree)
|
| 650 |
+
result = transform(etree.XML('<hello />'))
|
| 651 |
+
self.assertEqual('world', result.getroot().tag)
|
| 652 |
+
|
| 653 |
+
def test_text_output(self):
|
| 654 |
+
with open('text-output.xsl') as f:
|
| 655 |
+
xsl_tree = etree.parse(f)
|
| 656 |
+
transform = etree.XSLT(xsl_tree)
|
| 657 |
+
result = transform(etree.XML('<hello/>'))
|
| 658 |
+
self.assertEqual(None, result.getroot())
|
| 659 |
+
self.assertEqual(u'world', unicode(result))
|
| 660 |
+
self.assertEqual('world', str(result))
|
| 661 |
+
|
| 662 |
+
|
| 663 |
+
if __name__ == '__main__':
|
| 664 |
+
unittest.main()
|
tools/jxml/tests/text-output.xsl
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<xsl:stylesheet version="1.0"
|
| 3 |
+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
| 4 |
+
>
|
| 5 |
+
<xsl:output method="text" encoding="utf-8" indent="no" />
|
| 6 |
+
<xsl:template match="/hello">world</xsl:template>
|
| 7 |
+
</xsl:stylesheet>
|
tools/jxml/tests/xsl/import-test.xsl
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<xsl:stylesheet version="1.0"
|
| 3 |
+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
| 4 |
+
>
|
| 5 |
+
<xsl:import href="imported.xsl" />
|
| 6 |
+
<xsl:output method="xml" encoding="utf-8" indent="no" />
|
| 7 |
+
<xsl:template match="/">
|
| 8 |
+
<xsl:apply-templates />
|
| 9 |
+
</xsl:template>
|
| 10 |
+
</xsl:stylesheet>
|
tools/jxml/tests/xsl/imported.xsl
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<xsl:stylesheet version="1.0"
|
| 3 |
+
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
| 4 |
+
>
|
| 5 |
+
<xsl:output method="xml" encoding="utf-8" indent="no" />
|
| 6 |
+
<xsl:template match="hello"><world /></xsl:template>
|
| 7 |
+
</xsl:stylesheet>
|
tools/oxt.tool/oxt_tool/__init__.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def console(soffice='soffice'):
|
| 6 |
+
import uno
|
| 7 |
+
import unokit.contexts
|
| 8 |
+
import unokit.services
|
| 9 |
+
import oxt_tool.remote
|
| 10 |
+
|
| 11 |
+
with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
|
| 12 |
+
desktop = unokit.services.css.frame.Desktop()
|
| 13 |
+
def new_textdoc():
|
| 14 |
+
return desktop.loadComponentFromURL('private:factory/swriter',
|
| 15 |
+
'_blank', 0, tuple())
|
| 16 |
+
from unokit.util import dump, dumpdir
|
| 17 |
+
local = dict(uno=uno, context=context,
|
| 18 |
+
css=unokit.services.css, dump=dump, dumpdir=dumpdir,
|
| 19 |
+
desktop=desktop, new_textdoc=new_textdoc)
|
| 20 |
+
__import__('code').interact(banner='oxt-console', local=local)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def dict_to_namedvalue(d):
|
| 24 |
+
import uno
|
| 25 |
+
nv = list()
|
| 26 |
+
for n, v in d.items():
|
| 27 |
+
if isinstance(v, dict):
|
| 28 |
+
v = dict_to_namedvalue(v)
|
| 29 |
+
item = uno.createUnoStruct('com.sun.star.beans.NamedValue')
|
| 30 |
+
item.Name = n
|
| 31 |
+
item.Value = v
|
| 32 |
+
nv.append(item)
|
| 33 |
+
return tuple(nv)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def test_remotely(soffice, discover_dirs, extra_path, logconf_path):
|
| 37 |
+
import sys
|
| 38 |
+
import os
|
| 39 |
+
import os.path
|
| 40 |
+
import discover
|
| 41 |
+
import oxt_tool.remote
|
| 42 |
+
|
| 43 |
+
logger = logging.getLogger('unokit')
|
| 44 |
+
logger.addHandler(logging.StreamHandler())
|
| 45 |
+
logger.setLevel(logging.INFO)
|
| 46 |
+
|
| 47 |
+
logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
|
| 48 |
+
+'%(message)s')
|
| 49 |
+
logchn = logging.StreamHandler()
|
| 50 |
+
logchn.setFormatter(logfmt)
|
| 51 |
+
logger = logging.getLogger('frontend')
|
| 52 |
+
logger.addHandler(logchn)
|
| 53 |
+
logger.setLevel(logging.INFO)
|
| 54 |
+
|
| 55 |
+
working_dir = sys.argv[1]
|
| 56 |
+
working_dir = os.path.abspath(working_dir)
|
| 57 |
+
|
| 58 |
+
for path in sys.path:
|
| 59 |
+
logger.info('sys.path: %s', path)
|
| 60 |
+
|
| 61 |
+
if logconf_path:
|
| 62 |
+
logconf_path = os.path.abspath(logconf_path)
|
| 63 |
+
|
| 64 |
+
backend_path = sys.modules['oxt_tool'].__file__
|
| 65 |
+
backend_path = os.path.dirname(backend_path)
|
| 66 |
+
backend_path = os.path.join(backend_path, 'backend.py')
|
| 67 |
+
backend_name = 'backend.TestRunnerJob'
|
| 68 |
+
|
| 69 |
+
tss = []
|
| 70 |
+
for d in discover_dirs:
|
| 71 |
+
d = os.path.abspath(d)
|
| 72 |
+
logger.info('discover tests: %s', d)
|
| 73 |
+
testloader = discover.DiscoveringTestLoader()
|
| 74 |
+
testsuite = testloader.discover(d)
|
| 75 |
+
tss.append(testsuite)
|
| 76 |
+
import unittest
|
| 77 |
+
testsuite = unittest.TestSuite(tss)
|
| 78 |
+
|
| 79 |
+
with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
|
| 80 |
+
logger.info('remote context created')
|
| 81 |
+
factory = load_component(backend_path, backend_name)
|
| 82 |
+
if factory:
|
| 83 |
+
backendjob = factory.createInstanceWithContext(context)
|
| 84 |
+
if backendjob:
|
| 85 |
+
import cPickle
|
| 86 |
+
from unokit.adapters import OutputStreamToFileLike
|
| 87 |
+
pickled_testsuite = cPickle.dumps(testsuite)
|
| 88 |
+
outputstream = OutputStreamToFileLike(sys.stderr)
|
| 89 |
+
logstream = OutputStreamToFileLike(sys.stderr)
|
| 90 |
+
args = dict(outputstream=outputstream,
|
| 91 |
+
logstream=logstream,
|
| 92 |
+
pickled_testsuite=pickled_testsuite,
|
| 93 |
+
extra_path=tuple(extra_path),
|
| 94 |
+
logconf_path=logconf_path,
|
| 95 |
+
working_dir=working_dir)
|
| 96 |
+
args = dict_to_namedvalue(args)
|
| 97 |
+
result = backendjob.execute(args)
|
| 98 |
+
result = str(result)
|
| 99 |
+
result = cPickle.loads(result)
|
| 100 |
+
return 0 if result['successful'] else 1
|
| 101 |
+
return -1
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def load_component(component_path, component_name):
|
| 105 |
+
import os
|
| 106 |
+
import os.path
|
| 107 |
+
import uno
|
| 108 |
+
from unokit.services import css
|
| 109 |
+
loader = css.loader.Python()
|
| 110 |
+
if loader:
|
| 111 |
+
component_path = os.path.abspath(component_path)
|
| 112 |
+
component_url = uno.systemPathToFileUrl(component_path)
|
| 113 |
+
|
| 114 |
+
return loader.activate(component_name, '',
|
| 115 |
+
component_url, None)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def console_in_proc(soffice='soffice'):
|
| 119 |
+
import os
|
| 120 |
+
import unohelper
|
| 121 |
+
from com.sun.star.task import XJob
|
| 122 |
+
import oxt_tool.remote
|
| 123 |
+
|
| 124 |
+
logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
|
| 125 |
+
+'%(message)s')
|
| 126 |
+
logchn = logging.StreamHandler()
|
| 127 |
+
logchn.setFormatter(logfmt)
|
| 128 |
+
logger = logging.getLogger('frontend')
|
| 129 |
+
logger.addHandler(logchn)
|
| 130 |
+
logger.setLevel(logging.INFO)
|
| 131 |
+
|
| 132 |
+
class ConsoleInput(unohelper.Base, XJob):
|
| 133 |
+
|
| 134 |
+
def __init__(self, context):
|
| 135 |
+
self.context = context
|
| 136 |
+
|
| 137 |
+
def execute(self, arguments):
|
| 138 |
+
prompt, = arguments
|
| 139 |
+
try:
|
| 140 |
+
return raw_input(prompt.Value)
|
| 141 |
+
except EOFError:
|
| 142 |
+
return None
|
| 143 |
+
|
| 144 |
+
import sys
|
| 145 |
+
import os.path
|
| 146 |
+
backend_path = sys.modules['oxt_tool'].__file__
|
| 147 |
+
backend_path = os.path.dirname(backend_path)
|
| 148 |
+
backend_path = os.path.join(backend_path, 'backend.py')
|
| 149 |
+
backend_name = 'backend.ConsoleJob'
|
| 150 |
+
|
| 151 |
+
with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
|
| 152 |
+
logger.info('remote context created')
|
| 153 |
+
factory = load_component(backend_path, backend_name)
|
| 154 |
+
if factory:
|
| 155 |
+
backendjob = factory.createInstanceWithContext(context)
|
| 156 |
+
if backendjob:
|
| 157 |
+
from unokit.adapters import OutputStreamToFileLike
|
| 158 |
+
outstream = OutputStreamToFileLike(sys.stderr)
|
| 159 |
+
args = dict(inp=ConsoleInput(context),
|
| 160 |
+
outstream=outstream)
|
| 161 |
+
args = dict_to_namedvalue(args)
|
| 162 |
+
backendjob.execute(args)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
class LoEnviron(object):
|
| 166 |
+
|
| 167 |
+
def __init__(self, program_dir):
|
| 168 |
+
self.program_dir = program_dir
|
| 169 |
+
|
| 170 |
+
@property
|
| 171 |
+
def rc_ext(self):
|
| 172 |
+
import sys
|
| 173 |
+
if sys.platform == 'win32':
|
| 174 |
+
return '.ini'
|
| 175 |
+
else:
|
| 176 |
+
return 'rc'
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
@property
|
| 180 |
+
def fundamentalrc(self):
|
| 181 |
+
import os.path
|
| 182 |
+
filename = 'fundamental' + self.rc_ext
|
| 183 |
+
return os.path.join(self.program_dir, filename)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
@property
|
| 187 |
+
def ure_bootstrap(self):
|
| 188 |
+
return 'vnd.sun.star.pathname:' + self.fundamentalrc
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def run_in_lo(soffice='soffice'):
|
| 192 |
+
import os
|
| 193 |
+
import sys
|
| 194 |
+
import os.path
|
| 195 |
+
|
| 196 |
+
uno_pythonpath = os.environ['UNO_PYTHONPATH'].split(os.pathsep)
|
| 197 |
+
sys.path.extend(uno_pythonpath)
|
| 198 |
+
|
| 199 |
+
loenv = LoEnviron(os.environ['LO_PROGRAM'])
|
| 200 |
+
os.environ['URE_BOOTSTRAP'] = loenv.ure_bootstrap
|
| 201 |
+
|
| 202 |
+
import oxt_tool.remote
|
| 203 |
+
|
| 204 |
+
logger = logging.getLogger('unokit')
|
| 205 |
+
logger.addHandler(logging.StreamHandler())
|
| 206 |
+
logger.setLevel(logging.INFO)
|
| 207 |
+
|
| 208 |
+
logger = logging.getLogger('oxt_tool')
|
| 209 |
+
logger.addHandler(logging.StreamHandler())
|
| 210 |
+
logger.setLevel(logging.INFO)
|
| 211 |
+
|
| 212 |
+
logfmt = logging.Formatter(('frontend %5d ' % os.getpid())
|
| 213 |
+
+'%(message)s')
|
| 214 |
+
logchn = logging.StreamHandler()
|
| 215 |
+
logchn.setFormatter(logfmt)
|
| 216 |
+
logger = logging.getLogger('frontend')
|
| 217 |
+
logger.addHandler(logchn)
|
| 218 |
+
logger.setLevel(logging.INFO)
|
| 219 |
+
|
| 220 |
+
for path in sys.path:
|
| 221 |
+
logger.info('sys.path: %s', path)
|
| 222 |
+
|
| 223 |
+
working_dir = os.getcwd()
|
| 224 |
+
working_dir = os.path.abspath(working_dir)
|
| 225 |
+
argv = list(sys.argv[1:])
|
| 226 |
+
|
| 227 |
+
if argv[0].startswith('--logfile='):
|
| 228 |
+
logfile = argv[0][len('--logfile='):]
|
| 229 |
+
argv = argv[1:]
|
| 230 |
+
else:
|
| 231 |
+
logfile = None
|
| 232 |
+
argv[0] = os.path.abspath(argv[0])
|
| 233 |
+
print argv
|
| 234 |
+
|
| 235 |
+
backend_path = sys.modules['oxt_tool'].__file__
|
| 236 |
+
backend_path = os.path.dirname(backend_path)
|
| 237 |
+
backend_path = os.path.join(backend_path, 'backend.py')
|
| 238 |
+
backend_name = 'backend.RemoteRunJob'
|
| 239 |
+
|
| 240 |
+
with oxt_tool.remote.new_remote_context(soffice=soffice) as context:
|
| 241 |
+
logger.info('remote context created')
|
| 242 |
+
factory = load_component(backend_path, backend_name)
|
| 243 |
+
if factory:
|
| 244 |
+
backendjob = factory.createInstanceWithContext(context)
|
| 245 |
+
if backendjob:
|
| 246 |
+
import cPickle
|
| 247 |
+
from unokit.adapters import OutputStreamToFileLike
|
| 248 |
+
from unokit.adapters import InputStreamFromFileLike
|
| 249 |
+
stdin = InputStreamFromFileLike(sys.stdin)
|
| 250 |
+
stdout = OutputStreamToFileLike(sys.stdout)
|
| 251 |
+
stderr = OutputStreamToFileLike(sys.stderr)
|
| 252 |
+
path = cPickle.dumps(sys.path)
|
| 253 |
+
argv = cPickle.dumps(argv)
|
| 254 |
+
args = dict(logfile=logfile,
|
| 255 |
+
working_dir=working_dir,
|
| 256 |
+
path=path,
|
| 257 |
+
argv=argv,
|
| 258 |
+
stdin=stdin,
|
| 259 |
+
stdout=stdout,
|
| 260 |
+
stderr=stderr)
|
| 261 |
+
args = dict_to_namedvalue(args)
|
| 262 |
+
return backendjob.execute(args)
|
| 263 |
+
return -1
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def install(unopkg='unopkg'):
|
| 267 |
+
import sys
|
| 268 |
+
cmd = [unopkg, 'add', '-v', '-s']
|
| 269 |
+
cmd.extend(sys.argv[1:])
|
| 270 |
+
cmd = [('"%s"' % x) if ' ' not in x else x
|
| 271 |
+
for x in cmd]
|
| 272 |
+
cmd = ' '.join(cmd)
|
| 273 |
+
import os
|
| 274 |
+
return os.system(cmd)
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
class Console(object):
|
| 278 |
+
def __init__(self, buildout, name, options):
|
| 279 |
+
import os.path
|
| 280 |
+
self.__name = name
|
| 281 |
+
self.__logger = logging.getLogger(name)
|
| 282 |
+
self.__bindir = buildout['buildout']['bin-directory']
|
| 283 |
+
|
| 284 |
+
soffice = options.get('soffice', 'soffice').strip()
|
| 285 |
+
if not os.path.exists(soffice):
|
| 286 |
+
self.__skip = True
|
| 287 |
+
self.__logger.info('soffice not found at: %s', soffice)
|
| 288 |
+
self.__logger.info('installation will be skipped')
|
| 289 |
+
return
|
| 290 |
+
|
| 291 |
+
in_proc = options.get('in_proc', 'true').strip().lower()
|
| 292 |
+
in_proc = in_proc in ['true', 'yes', '1']
|
| 293 |
+
|
| 294 |
+
self.__python = options.get('python', '').strip()
|
| 295 |
+
self.__soffice = soffice
|
| 296 |
+
self.__in_proc = in_proc
|
| 297 |
+
self.__skip = False
|
| 298 |
+
|
| 299 |
+
def install(self):
|
| 300 |
+
if self.__skip:
|
| 301 |
+
self.__logger.info('skipped')
|
| 302 |
+
return []
|
| 303 |
+
|
| 304 |
+
from zc.buildout import easy_install
|
| 305 |
+
import pkg_resources
|
| 306 |
+
import sys
|
| 307 |
+
ws = [pkg_resources.get_distribution(dist)
|
| 308 |
+
for dist in ['unokit', 'oxt.tool']]
|
| 309 |
+
if self.__in_proc:
|
| 310 |
+
func = 'console_in_proc'
|
| 311 |
+
else:
|
| 312 |
+
func = 'console'
|
| 313 |
+
entrypoints = [(self.__name, 'oxt_tool', func)]
|
| 314 |
+
arguments = '%r' % self.__soffice
|
| 315 |
+
if self.__python:
|
| 316 |
+
python = self.__python
|
| 317 |
+
else:
|
| 318 |
+
python = sys.executable
|
| 319 |
+
return easy_install.scripts(entrypoints,
|
| 320 |
+
ws, python, self.__bindir,
|
| 321 |
+
arguments=arguments)
|
| 322 |
+
|
| 323 |
+
update = install
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
class TestRunner(object):
|
| 327 |
+
def __init__(self, buildout, name, options):
|
| 328 |
+
import os.path
|
| 329 |
+
self.__name = name
|
| 330 |
+
self.__logger = logging.getLogger(name)
|
| 331 |
+
self.__bindir = buildout['buildout']['bin-directory']
|
| 332 |
+
self.__skip = False
|
| 333 |
+
self.__python = options.get('python', '').strip()
|
| 334 |
+
|
| 335 |
+
self.__soffice = options.get('soffice', 'soffice').strip()
|
| 336 |
+
if not os.path.exists(self.__soffice):
|
| 337 |
+
self.__skip = True
|
| 338 |
+
self.__logger.info('soffice not found at: %s', self.__soffice)
|
| 339 |
+
self.__logger.info('installation will be skipped')
|
| 340 |
+
return
|
| 341 |
+
self.__discover = options['discover'].split()
|
| 342 |
+
self.__extra_path = options['extra_path'].split()
|
| 343 |
+
self.__logconf_path = options.get('logconf_path')
|
| 344 |
+
|
| 345 |
+
def install(self):
|
| 346 |
+
if self.__skip:
|
| 347 |
+
self.__logger.info('skipped')
|
| 348 |
+
return []
|
| 349 |
+
|
| 350 |
+
from zc.buildout import easy_install
|
| 351 |
+
import pkg_resources
|
| 352 |
+
import sys
|
| 353 |
+
ws = [pkg_resources.get_distribution(dist)
|
| 354 |
+
for dist in ['unokit', 'oxt.tool', 'discover']]
|
| 355 |
+
entrypoints = [(self.__name, 'oxt_tool', 'test_remotely')]
|
| 356 |
+
arguments = '%r, %r, %r, %r'
|
| 357 |
+
arguments = arguments % (self.__soffice,
|
| 358 |
+
self.__discover,
|
| 359 |
+
self.__extra_path,
|
| 360 |
+
self.__logconf_path)
|
| 361 |
+
if self.__python:
|
| 362 |
+
python = self.__python
|
| 363 |
+
else:
|
| 364 |
+
python = sys.executable
|
| 365 |
+
return easy_install.scripts(entrypoints,
|
| 366 |
+
ws, python, self.__bindir,
|
| 367 |
+
arguments=arguments)
|
| 368 |
+
|
| 369 |
+
update = install
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
class Installer(object):
|
| 373 |
+
def __init__(self, buildout, name, options):
|
| 374 |
+
import os.path
|
| 375 |
+
self.__name = name
|
| 376 |
+
self.__logger = logging.getLogger(name)
|
| 377 |
+
self.__bindir = buildout['buildout']['bin-directory']
|
| 378 |
+
|
| 379 |
+
unopkg = options.get('unopkg', 'unopkg').strip()
|
| 380 |
+
if not os.path.exists(unopkg):
|
| 381 |
+
self.__skip = True
|
| 382 |
+
self.__logger.info('unopkg not found at: %s', unopkg)
|
| 383 |
+
self.__logger.info('installation will be skipped')
|
| 384 |
+
return
|
| 385 |
+
|
| 386 |
+
self.__unopkg = unopkg
|
| 387 |
+
self.__skip = False
|
| 388 |
+
|
| 389 |
+
def install(self):
|
| 390 |
+
if self.__skip:
|
| 391 |
+
self.__logger.info('skipped')
|
| 392 |
+
return []
|
| 393 |
+
|
| 394 |
+
from zc.buildout import easy_install
|
| 395 |
+
import pkg_resources
|
| 396 |
+
import sys
|
| 397 |
+
ws = [pkg_resources.get_distribution(dist)
|
| 398 |
+
for dist in ['unokit', 'oxt.tool']]
|
| 399 |
+
entrypoints = [(self.__name, 'oxt_tool', 'install')]
|
| 400 |
+
arguments = '%r' % self.__unopkg
|
| 401 |
+
return easy_install.scripts(entrypoints,
|
| 402 |
+
ws, sys.executable, self.__bindir,
|
| 403 |
+
arguments=arguments)
|
| 404 |
+
|
| 405 |
+
update = install
|
tools/oxt.tool/oxt_tool/backend.py
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
import logging
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
logfmt = logging.Formatter((' backend %5d ' % os.getpid())
|
| 7 |
+
+'%(message)s')
|
| 8 |
+
logger = logging.getLogger('backend')
|
| 9 |
+
logger.setLevel(logging.INFO)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
logger.info('backend: %s', os.getpid())
|
| 13 |
+
logger.info('sys.executable = %s', sys.executable)
|
| 14 |
+
|
| 15 |
+
import unohelper
|
| 16 |
+
g_ImplementationHelper = unohelper.ImplementationHelper()
|
| 17 |
+
|
| 18 |
+
def implementation(component_name, *services):
|
| 19 |
+
def decorator(cls):
|
| 20 |
+
g_ImplementationHelper.addImplementation(cls, component_name, services)
|
| 21 |
+
return cls
|
| 22 |
+
return decorator
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
from com.sun.star.task import XJob
|
| 26 |
+
|
| 27 |
+
@implementation('backend.TestRunnerJob')
|
| 28 |
+
class TestRunnerJob(unohelper.Base, XJob):
|
| 29 |
+
|
| 30 |
+
def __init__(self, context):
|
| 31 |
+
self.context = context
|
| 32 |
+
|
| 33 |
+
def execute(self, arguments):
|
| 34 |
+
import sys
|
| 35 |
+
args = dict((nv.Name, nv.Value) for nv in arguments)
|
| 36 |
+
|
| 37 |
+
cwd = os.getcwd()
|
| 38 |
+
working_dir = args['working_dir']
|
| 39 |
+
os.chdir(working_dir)
|
| 40 |
+
try:
|
| 41 |
+
logstream = args['logstream']
|
| 42 |
+
logstream = FileFromStream(logstream)
|
| 43 |
+
loghandler = logging.StreamHandler(logstream)
|
| 44 |
+
loghandler.setFormatter(logfmt)
|
| 45 |
+
logger.addHandler(loghandler)
|
| 46 |
+
try:
|
| 47 |
+
logger.info('current dir: %s', cwd)
|
| 48 |
+
logger.info('working dir: %s', working_dir)
|
| 49 |
+
logger.info('sys.path:')
|
| 50 |
+
for x in sys.path:
|
| 51 |
+
logger.info('- %s', x)
|
| 52 |
+
return self.run(args)
|
| 53 |
+
finally:
|
| 54 |
+
logger.removeHandler(loghandler)
|
| 55 |
+
finally:
|
| 56 |
+
os.chdir(cwd)
|
| 57 |
+
|
| 58 |
+
def run(self, args):
|
| 59 |
+
import cPickle
|
| 60 |
+
outstream = args.get('outputstream')
|
| 61 |
+
outstream = FileFromStream(outstream)
|
| 62 |
+
|
| 63 |
+
extra_path = args.get('extra_path')
|
| 64 |
+
if extra_path:
|
| 65 |
+
logger.info('extra_path: %s', ' '.join(extra_path))
|
| 66 |
+
sys.path.extend(extra_path)
|
| 67 |
+
|
| 68 |
+
logconf_path = args.get('logconf_path')
|
| 69 |
+
if logconf_path:
|
| 70 |
+
import logging.config
|
| 71 |
+
logging.config.fileConfig(logconf_path)
|
| 72 |
+
|
| 73 |
+
from hwp5.plat import _uno
|
| 74 |
+
_uno.enable()
|
| 75 |
+
|
| 76 |
+
pickled_testsuite = args.get('pickled_testsuite')
|
| 77 |
+
if not pickled_testsuite:
|
| 78 |
+
logger.error('pickled_testsuite is required')
|
| 79 |
+
return cPickle.dumps(dict(successful=False, tests=0, failures=0,
|
| 80 |
+
errors=0))
|
| 81 |
+
|
| 82 |
+
pickled_testsuite = str(pickled_testsuite)
|
| 83 |
+
testsuite = cPickle.loads(pickled_testsuite)
|
| 84 |
+
logger.info('Test Suite Unpickled')
|
| 85 |
+
|
| 86 |
+
from unittest import TextTestRunner
|
| 87 |
+
testrunner = TextTestRunner(stream=outstream)
|
| 88 |
+
result = testrunner.run(testsuite)
|
| 89 |
+
result = dict(successful=result.wasSuccessful(),
|
| 90 |
+
tests=result.testsRun,
|
| 91 |
+
failures=list(str(x) for x in result.failures),
|
| 92 |
+
errors=list(str(x) for x in result.errors))
|
| 93 |
+
return cPickle.dumps(result)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
import contextlib
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@contextlib.contextmanager
|
| 100 |
+
def sandbox(working_dir, **kwargs):
|
| 101 |
+
import os
|
| 102 |
+
import sys
|
| 103 |
+
|
| 104 |
+
backup = dict()
|
| 105 |
+
class NOTHING:
|
| 106 |
+
pass
|
| 107 |
+
if not hasattr(sys, 'argv'):
|
| 108 |
+
sys.argv = NOTHING
|
| 109 |
+
|
| 110 |
+
NAMES = ['path', 'argv', 'stdin', 'stdout', 'stderr']
|
| 111 |
+
for x in NAMES:
|
| 112 |
+
assert x in kwargs
|
| 113 |
+
|
| 114 |
+
backup['cwd'] = os.getcwd()
|
| 115 |
+
os.chdir(working_dir)
|
| 116 |
+
for x in NAMES:
|
| 117 |
+
backup[x] = getattr(sys, x)
|
| 118 |
+
setattr(sys, x, kwargs[x])
|
| 119 |
+
|
| 120 |
+
try:
|
| 121 |
+
yield
|
| 122 |
+
finally:
|
| 123 |
+
for x in NAMES:
|
| 124 |
+
setattr(sys, x, backup[x])
|
| 125 |
+
os.chdir(backup['cwd'])
|
| 126 |
+
|
| 127 |
+
if sys.argv is NOTHING:
|
| 128 |
+
del sys.argv
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
@implementation('backend.RemoteRunJob')
|
| 132 |
+
class RemoteRunJob(unohelper.Base, XJob):
|
| 133 |
+
|
| 134 |
+
def __init__(self, context):
|
| 135 |
+
self.context = context
|
| 136 |
+
|
| 137 |
+
def execute(self, arguments):
|
| 138 |
+
args = dict((nv.Name, nv.Value) for nv in arguments)
|
| 139 |
+
|
| 140 |
+
logpath = args.get('logfile')
|
| 141 |
+
if logpath is not None:
|
| 142 |
+
logfile = file(logpath, 'a')
|
| 143 |
+
loghandler = logging.StreamHandler(logfile)
|
| 144 |
+
logger.addHandler(loghandler)
|
| 145 |
+
|
| 146 |
+
import datetime
|
| 147 |
+
logger.info('-'*10 + (' start at %s' % datetime.datetime.now()) + '-'*10)
|
| 148 |
+
try:
|
| 149 |
+
return self.run(args)
|
| 150 |
+
finally:
|
| 151 |
+
logger.info('-'*10 + (' stop at %s' % datetime.datetime.now()) + '-'*10)
|
| 152 |
+
if logpath is not None:
|
| 153 |
+
logger.removeHandler(loghandler)
|
| 154 |
+
|
| 155 |
+
def run(self, args):
|
| 156 |
+
import cPickle
|
| 157 |
+
|
| 158 |
+
working_dir = args['working_dir']
|
| 159 |
+
path = cPickle.loads(str(args['path']))
|
| 160 |
+
argv = cPickle.loads(str(args['argv']))
|
| 161 |
+
stdin = FileFromStream(args['stdin'])
|
| 162 |
+
stdout = FileFromStream(args['stdout'])
|
| 163 |
+
stderr = FileFromStream(args['stderr'])
|
| 164 |
+
|
| 165 |
+
script = argv[0]
|
| 166 |
+
with sandbox(working_dir, path=path, argv=argv, stdin=stdin,
|
| 167 |
+
stdout=stdout, stderr=stderr):
|
| 168 |
+
g = dict(__name__='__main__')
|
| 169 |
+
try:
|
| 170 |
+
execfile(script, g)
|
| 171 |
+
except SystemExit, e:
|
| 172 |
+
return e.code
|
| 173 |
+
except Exception, e:
|
| 174 |
+
logger.exception(e)
|
| 175 |
+
raise
|
| 176 |
+
except:
|
| 177 |
+
import traceback
|
| 178 |
+
logger.error('%s' % traceback.format_exc())
|
| 179 |
+
raise
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
@implementation('backend.ConsoleJob')
|
| 183 |
+
class ConsoleJob(unohelper.Base, XJob):
|
| 184 |
+
|
| 185 |
+
def __init__(self, context):
|
| 186 |
+
self.context = context
|
| 187 |
+
|
| 188 |
+
def execute(self, arguments):
|
| 189 |
+
args = dict((nv.Name, nv.Value) for nv in arguments)
|
| 190 |
+
|
| 191 |
+
cwd = os.getcwd()
|
| 192 |
+
try:
|
| 193 |
+
inp = args['inp']
|
| 194 |
+
outstream = args['outstream']
|
| 195 |
+
|
| 196 |
+
outfile = FileFromStream(outstream)
|
| 197 |
+
|
| 198 |
+
import sys
|
| 199 |
+
orig = sys.stdout, sys.stderr
|
| 200 |
+
sys.stdout = sys.stderr = outfile
|
| 201 |
+
try:
|
| 202 |
+
console = Console(inp, outfile)
|
| 203 |
+
try:
|
| 204 |
+
console.interact('LibreOffice Python Console (pid: %s)' %
|
| 205 |
+
os.getpid())
|
| 206 |
+
return 0
|
| 207 |
+
except SystemExit, e:
|
| 208 |
+
return e.code
|
| 209 |
+
finally:
|
| 210 |
+
sys.stdout, sys.stderr = orig
|
| 211 |
+
finally:
|
| 212 |
+
os.chdir(cwd)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
from code import InteractiveConsole
|
| 216 |
+
class Console(InteractiveConsole):
|
| 217 |
+
|
| 218 |
+
def __init__(self, inp, outfile):
|
| 219 |
+
InteractiveConsole.__init__(self)
|
| 220 |
+
self.inp = inp
|
| 221 |
+
self.outfile = outfile
|
| 222 |
+
|
| 223 |
+
def write(self, data):
|
| 224 |
+
self.outfile.write(data)
|
| 225 |
+
self.outfile.flush()
|
| 226 |
+
|
| 227 |
+
def raw_input(self, prompt=''):
|
| 228 |
+
import uno
|
| 229 |
+
arg = uno.createUnoStruct('com.sun.star.beans.NamedValue')
|
| 230 |
+
arg.Name = 'prompt'
|
| 231 |
+
arg.Value = prompt
|
| 232 |
+
args = arg,
|
| 233 |
+
result = self.inp.execute(args)
|
| 234 |
+
if result is None:
|
| 235 |
+
raise EOFError()
|
| 236 |
+
return result
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
class FileFromStream(object):
|
| 240 |
+
''' A file-like object based on XInputStream/XOuputStream/XSeekable
|
| 241 |
+
|
| 242 |
+
:param stream: a stream object which implements
|
| 243 |
+
com.sun.star.io.XInputStream, com.sun.star.io.XOutputStream or
|
| 244 |
+
com.sun.star.io.XSeekable
|
| 245 |
+
'''
|
| 246 |
+
def __init__(self, stream, encoding='utf-8'):
|
| 247 |
+
import uno
|
| 248 |
+
self.stream = stream
|
| 249 |
+
self.encoding = encoding
|
| 250 |
+
|
| 251 |
+
if hasattr(stream, 'readBytes'):
|
| 252 |
+
def read(size=None):
|
| 253 |
+
if size is None:
|
| 254 |
+
data = ''
|
| 255 |
+
while True:
|
| 256 |
+
bytes = uno.ByteSequence('')
|
| 257 |
+
n_read, bytes = stream.readBytes(bytes, 4096)
|
| 258 |
+
if n_read == 0:
|
| 259 |
+
return data
|
| 260 |
+
data += bytes.value
|
| 261 |
+
bytes = uno.ByteSequence('')
|
| 262 |
+
n_read, bytes = stream.readBytes(bytes, size)
|
| 263 |
+
return bytes.value
|
| 264 |
+
self.read = read
|
| 265 |
+
|
| 266 |
+
if hasattr(stream, 'seek'):
|
| 267 |
+
self.tell = stream.getPosition
|
| 268 |
+
|
| 269 |
+
def seek(offset, whence=0):
|
| 270 |
+
if whence == 0:
|
| 271 |
+
pass
|
| 272 |
+
elif whence == 1:
|
| 273 |
+
offset += stream.getPosition()
|
| 274 |
+
elif whence == 2:
|
| 275 |
+
offset += stream.getLength()
|
| 276 |
+
stream.seek(offset)
|
| 277 |
+
self.seek = seek
|
| 278 |
+
|
| 279 |
+
if hasattr(stream, 'writeBytes'):
|
| 280 |
+
def write(s):
|
| 281 |
+
if isinstance(s, unicode):
|
| 282 |
+
s = s.encode(self.encoding)
|
| 283 |
+
stream.writeBytes(uno.ByteSequence(s))
|
| 284 |
+
self.write = write
|
| 285 |
+
|
| 286 |
+
def flush():
|
| 287 |
+
stream.flush()
|
| 288 |
+
self.flush = flush
|
| 289 |
+
|
| 290 |
+
def close(self):
|
| 291 |
+
if hasattr(self.stream, 'closeInput'):
|
| 292 |
+
self.stream.closeInput()
|
| 293 |
+
elif hasattr(self.stream, 'closeOutput'):
|
| 294 |
+
self.stream.closeOutput()
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
'''
|
| 298 |
+
import os.path
|
| 299 |
+
from uno import systemPathToFileUrl
|
| 300 |
+
from unokit.ucb import open_url
|
| 301 |
+
from unokit.services import css
|
| 302 |
+
|
| 303 |
+
path = os.path.abspath('samples/sample-5017.hwp')
|
| 304 |
+
print path
|
| 305 |
+
url = systemPathToFileUrl(path)
|
| 306 |
+
print url
|
| 307 |
+
inp = open_url(url)
|
| 308 |
+
print inp
|
| 309 |
+
|
| 310 |
+
# 여기서 segfault
|
| 311 |
+
stg = css.embed.OLESimpleStorage(inp)
|
| 312 |
+
print stg
|
| 313 |
+
'''
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
'''
|
| 317 |
+
SegFault가 나는 stacktrace는 다음과 같다
|
| 318 |
+
|
| 319 |
+
StgDirEntry::StgDirEntry
|
| 320 |
+
sot/source/sdstor/
|
| 321 |
+
StgEntry::Load
|
| 322 |
+
sot/source/sdstor/
|
| 323 |
+
ToUpperUnicode
|
| 324 |
+
sot/source/sdstor/stgelem.cxx
|
| 325 |
+
CharClass
|
| 326 |
+
unotools/source/i18n/charclass.cxx
|
| 327 |
+
intl_createInstance
|
| 328 |
+
unotools/source/i18n/instance.hxx
|
| 329 |
+
|
| 330 |
+
여기서 ::comphelper::getProcessServiceFactory로 얻은 XMultiServiceFactory가
|
| 331 |
+
null 값인 것 같다.
|
| 332 |
+
|
| 333 |
+
----
|
| 334 |
+
|
| 335 |
+
uno를 사용하는 프로그램들 (desktop app/unopkg, 각종 unittest 프로그램)은
|
| 336 |
+
처음 실행 시 다음과 같은 과정을 거치는 듯 하다.
|
| 337 |
+
|
| 338 |
+
1. ::cppu::defaultBootstrap_InitialComponentContext()을 호출, local context 생성
|
| 339 |
+
- unorc 등을 검색, application.rdb, user.rdb 등에 접근
|
| 340 |
+
|
| 341 |
+
- pyuno.so 의 getComponentContext()에서 수행: PYUNOLIBDIR 즉 pyuno.so가 있는
|
| 342 |
+
디렉토리에서 pyuno.rc가 있으면 그것으로 초기화)
|
| 343 |
+
- desktop app: appinit.cxx의 CreateApplicationServiceManager()에서 수행
|
| 344 |
+
- unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 수행.
|
| 345 |
+
ucbhelper::ContentBroker도 함께 초기화한다.
|
| 346 |
+
|
| 347 |
+
2. 이렇게 생성한 local context로부터 ServiceManager를 얻어
|
| 348 |
+
::comphelper::setProcessServiceFactory()를 사용하여 프로세스 전역 service
|
| 349 |
+
factory로 등록
|
| 350 |
+
|
| 351 |
+
- uno.py와 pyuno.so는 이 작업을 하지 않는다.
|
| 352 |
+
- desktop app: app.cxx의 ensureProcessServiceFactory()에서 수행
|
| 353 |
+
- unopkg: unopkg_misc.cxx의 bootstrapStandAlone()에서 함께 수행.
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
* desktop app: desktop/source/app/
|
| 357 |
+
* unopkg: desktop/source/pkgchk/unopkg/
|
| 358 |
+
|
| 359 |
+
'''
|
| 360 |
+
|
| 361 |
+
logger.info('%s: end of file', __name__)
|
tools/oxt.tool/oxt_tool/description.py
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
from __future__ import with_statement
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
logger = logging.getLogger(__name__)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
NS_URI = 'http://openoffice.org/extensions/description/2006'
|
| 10 |
+
NS_URI_DEP = 'http://openoffice.org/extensions/description/2006'
|
| 11 |
+
NS_URI_XLINK = 'http://www.w3.org/1999/xlink'
|
| 12 |
+
|
| 13 |
+
NS = '{' + NS_URI + '}'
|
| 14 |
+
NS_DEP = '{' + NS_URI_DEP + '}'
|
| 15 |
+
NS_XLINK = '{' + NS_URI_XLINK + '}'
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def as_dict(f):
|
| 19 |
+
def wrapper(*args, **kwargs):
|
| 20 |
+
return dict(f(*args, **kwargs))
|
| 21 |
+
wrapper.items = f
|
| 22 |
+
return wrapper
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@as_dict
|
| 26 |
+
def get_display_name(doc):
|
| 27 |
+
root = doc.getroot()
|
| 28 |
+
for elt in root.findall(NS + 'display-name/' + NS + 'name'):
|
| 29 |
+
yield elt.get('lang'), elt.text
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def set_display_name(doc, display_name):
|
| 33 |
+
import xml.etree.ElementTree as ET
|
| 34 |
+
root = doc.getroot()
|
| 35 |
+
dispname = ET.SubElement(root, 'display-name')
|
| 36 |
+
for lang, name in display_name.items():
|
| 37 |
+
elt = ET.SubElement(dispname, 'name')
|
| 38 |
+
elt.set('lang', lang)
|
| 39 |
+
elt.text = name
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
@as_dict
|
| 43 |
+
def get_extension_description(doc):
|
| 44 |
+
root = doc.getroot()
|
| 45 |
+
for elt in root.findall(NS + 'extension-description/' + NS + 'src'):
|
| 46 |
+
yield elt.get('lang'), elt.get(NS_XLINK + 'href')
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def set_extension_description(doc, description):
|
| 50 |
+
import xml.etree.ElementTree as ET
|
| 51 |
+
root = doc.getroot()
|
| 52 |
+
desc = ET.SubElement(root, 'extension-description')
|
| 53 |
+
for lang, url in description.items():
|
| 54 |
+
elt = ET.SubElement(desc, 'src')
|
| 55 |
+
elt.set('lang', lang)
|
| 56 |
+
elt.set('xlink:href', url)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
@as_dict
|
| 60 |
+
def get_publisher(doc):
|
| 61 |
+
root = doc.getroot()
|
| 62 |
+
for elt in root.findall(NS + 'publisher/' + NS + 'name'):
|
| 63 |
+
yield elt.get('lang'), dict(name=elt.text,
|
| 64 |
+
url=elt.get(NS_XLINK + 'href'))
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def set_publisher(doc, publisher):
|
| 68 |
+
import xml.etree.ElementTree as ET
|
| 69 |
+
root = doc.getroot()
|
| 70 |
+
pub = ET.SubElement(root, 'publisher')
|
| 71 |
+
for lang, dct in publisher.items():
|
| 72 |
+
elt = ET.SubElement(pub, 'name')
|
| 73 |
+
elt.set('lang', lang)
|
| 74 |
+
elt.set('xlink:href', dct['url'])
|
| 75 |
+
elt.text = dct['name']
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def get_license_accept_by(doc):
|
| 79 |
+
root = doc.getroot()
|
| 80 |
+
for elt in root.findall(NS + 'registration/' + NS + 'simple-license'):
|
| 81 |
+
accept_by = elt.get('accept-by')
|
| 82 |
+
if accept_by:
|
| 83 |
+
return accept_by
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def get_license(doc):
|
| 87 |
+
root = doc.getroot()
|
| 88 |
+
for elt in root.findall(NS + 'registration/' + NS + 'simple-license'):
|
| 89 |
+
return dict((elt.get('lang'), elt.get(NS_XLINK + 'href'))
|
| 90 |
+
for elt in elt.findall(NS + 'license-text'))
|
| 91 |
+
return dict()
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def get_oo_min_version(doc):
|
| 95 |
+
root = doc.getroot()
|
| 96 |
+
for dep in root.findall(NS + 'dependencies'):
|
| 97 |
+
for elt in dep.findall(NS + 'OpenOffice.org-minimal-version'):
|
| 98 |
+
return elt.get('value')
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class Description(object):
|
| 102 |
+
|
| 103 |
+
@classmethod
|
| 104 |
+
def parse(cls, f):
|
| 105 |
+
import xml.etree.ElementTree as ET
|
| 106 |
+
|
| 107 |
+
doc = ET.parse(f)
|
| 108 |
+
root = doc.getroot()
|
| 109 |
+
|
| 110 |
+
def getvalue(xpath, default=None):
|
| 111 |
+
for elt in root.findall(xpath):
|
| 112 |
+
value = elt.get('value', default)
|
| 113 |
+
if value:
|
| 114 |
+
return value
|
| 115 |
+
return default
|
| 116 |
+
|
| 117 |
+
return cls(identifier=getvalue(NS + 'identifier'),
|
| 118 |
+
version=getvalue(NS + 'version'),
|
| 119 |
+
platform=getvalue(NS + 'platform', 'all'),
|
| 120 |
+
display_name=get_display_name(doc),
|
| 121 |
+
description=get_extension_description(doc),
|
| 122 |
+
publisher=get_publisher(doc),
|
| 123 |
+
license_accept_by=get_license_accept_by(doc),
|
| 124 |
+
license=get_license(doc),
|
| 125 |
+
oo_min_version=get_oo_min_version(doc))
|
| 126 |
+
|
| 127 |
+
def __init__(self,
|
| 128 |
+
identifier='noname',
|
| 129 |
+
version='0.0',
|
| 130 |
+
platform='all',
|
| 131 |
+
display_name=dict(),
|
| 132 |
+
description=dict(),
|
| 133 |
+
publisher=dict(),
|
| 134 |
+
license_accept_by='admin',
|
| 135 |
+
license=dict(),
|
| 136 |
+
oo_min_version=None):
|
| 137 |
+
''' Generate description.xml
|
| 138 |
+
|
| 139 |
+
:param f: output file
|
| 140 |
+
:param identifier: extension identifier
|
| 141 |
+
:param version: extension version
|
| 142 |
+
:param platform: target platform
|
| 143 |
+
:param display_name: localizations of display name
|
| 144 |
+
:param description: localizations of extension description
|
| 145 |
+
:param publisher: localizations of publisher
|
| 146 |
+
:param license_accept_by: who is supposed to accept the license
|
| 147 |
+
:param license: localization of license
|
| 148 |
+
:param oo_min_version: minimal version of LibreOffice
|
| 149 |
+
|
| 150 |
+
Each localization parameters are dicts, whose keys are language identifiers
|
| 151 |
+
defined in RFC 3066.
|
| 152 |
+
|
| 153 |
+
``identifier`` specifies `Extension Identifier
|
| 154 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extension_Identifiers>`_.
|
| 155 |
+
|
| 156 |
+
``version`` specifies `Extension Version
|
| 157 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extension_Versions>`_.
|
| 158 |
+
|
| 159 |
+
``platform`` specifies supposed `Target Platform
|
| 160 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Target_Platform>`_ on which this extension
|
| 161 |
+
runs. Default value is ``all``.
|
| 162 |
+
|
| 163 |
+
``display_name`` specifies localized `Display Names
|
| 164 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Display_Name>`_.
|
| 165 |
+
It's a localization dict whose values are localized unicode strings, e.g.::
|
| 166 |
+
|
| 167 |
+
display_name = {
|
| 168 |
+
'en': 'Example Filter',
|
| 169 |
+
'ko': u'예제 필터'
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
Values of ``description`` is a URL of description file, e.g.::
|
| 173 |
+
|
| 174 |
+
description = {
|
| 175 |
+
'en': 'description/en.txt',
|
| 176 |
+
'ko': 'description/ko.txt'
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
``publisher`` specifies `Publisher Information
|
| 180 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Publisher_Information>`_.
|
| 181 |
+
It's a localization dict whose values are dicts themselves, which have
|
| 182 |
+
``name`` and ``url``. ``name`` is a localized name of the publisher and
|
| 183 |
+
``url`` is a URL of the publisher. For example::
|
| 184 |
+
|
| 185 |
+
publisher = {
|
| 186 |
+
'en': {
|
| 187 |
+
'name': 'John Doe',
|
| 188 |
+
'url': 'http://example.tld'
|
| 189 |
+
},
|
| 190 |
+
'ko': {
|
| 191 |
+
'name': u'홍길동',
|
| 192 |
+
'url': 'http://example.tld'
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
Optional ``license_accept_by`` specifies who is supposed to accept the
|
| 197 |
+
license. ``admin`` or ``user``. Default value is 'admin'.
|
| 198 |
+
|
| 199 |
+
Optional ``license`` is a localization dict whose values are an URL of
|
| 200 |
+
license file. For example::
|
| 201 |
+
|
| 202 |
+
license = {
|
| 203 |
+
'en': 'registration/COPYING'
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
See `Simple License
|
| 207 |
+
<http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Simple_License>`_.
|
| 208 |
+
'''
|
| 209 |
+
self.identifier = identifier
|
| 210 |
+
self.version = version
|
| 211 |
+
self.platform = platform
|
| 212 |
+
self.display_name = display_name
|
| 213 |
+
self.description = description
|
| 214 |
+
self.publisher = publisher
|
| 215 |
+
self.license_accept_by = license_accept_by
|
| 216 |
+
self.license = license
|
| 217 |
+
self.oo_min_version = oo_min_version
|
| 218 |
+
|
| 219 |
+
def write(self, f):
|
| 220 |
+
|
| 221 |
+
# see http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Description_of_XML_Elements
|
| 222 |
+
|
| 223 |
+
import xml.etree.ElementTree as ET
|
| 224 |
+
|
| 225 |
+
root = ET.Element('description', {'xmlns': NS_URI,
|
| 226 |
+
'xmlns:dep': NS_URI_DEP,
|
| 227 |
+
'xmlns:xlink': NS_URI_XLINK})
|
| 228 |
+
doc = ET.ElementTree(root)
|
| 229 |
+
|
| 230 |
+
ET.SubElement(root, 'identifier').set('value', self.identifier)
|
| 231 |
+
ET.SubElement(root, 'version').set('value', self.version)
|
| 232 |
+
ET.SubElement(root, 'platform').set('value', self.platform)
|
| 233 |
+
|
| 234 |
+
set_display_name(doc, self.display_name)
|
| 235 |
+
|
| 236 |
+
set_extension_description(doc, self.description)
|
| 237 |
+
|
| 238 |
+
set_publisher(doc, self.publisher)
|
| 239 |
+
|
| 240 |
+
if self.license:
|
| 241 |
+
reg = ET.SubElement(root, 'registration')
|
| 242 |
+
lic = ET.SubElement(reg, 'simple-license')
|
| 243 |
+
lic.set('accept-by', self.license_accept_by)
|
| 244 |
+
for lang, url in self.license.items():
|
| 245 |
+
elt = ET.SubElement(lic, 'license-text')
|
| 246 |
+
elt.set('lang', lang)
|
| 247 |
+
elt.set('xlink:href', url)
|
| 248 |
+
|
| 249 |
+
if self.oo_min_version is not None:
|
| 250 |
+
dep = ET.SubElement(root, 'dependencies')
|
| 251 |
+
minver = ET.SubElement(dep, 'OpenOffice.org-minimal-version')
|
| 252 |
+
minver.set('dep:name', 'LibreOffice ' + self.oo_min_version)
|
| 253 |
+
minver.set('value', self.oo_min_version)
|
| 254 |
+
|
| 255 |
+
f.write('<?xml version="1.0" encoding="utf-8"?>')
|
| 256 |
+
doc.write(f, encoding='utf-8')
|
| 257 |
+
|
| 258 |
+
def required_files(self):
|
| 259 |
+
for url in self.description.values():
|
| 260 |
+
yield url
|
| 261 |
+
for url in self.license.values():
|
| 262 |
+
yield url
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def print_human_readable(desc, root_stg=None):
|
| 266 |
+
''' Print summary in human readable form.
|
| 267 |
+
|
| 268 |
+
:param desc: an instance of Description
|
| 269 |
+
:param root_stg: root storage of description.xml
|
| 270 |
+
'''
|
| 271 |
+
from storage import resolve_path
|
| 272 |
+
print 'identifier:', desc.identifier
|
| 273 |
+
print 'version:', desc.version
|
| 274 |
+
print 'platform:', desc.platform
|
| 275 |
+
|
| 276 |
+
print 'display-name:'
|
| 277 |
+
for lang, name in desc.display_name.items():
|
| 278 |
+
print ' [%s] %s' % (lang, name)
|
| 279 |
+
|
| 280 |
+
print 'extension-description:'
|
| 281 |
+
for lang, url in desc.description.items():
|
| 282 |
+
if not root_stg or resolve_path(root_stg, url):
|
| 283 |
+
state = ''
|
| 284 |
+
else:
|
| 285 |
+
state = ' -- MISSING'
|
| 286 |
+
print ' [%s] %s%s' % (lang, url, state)
|
| 287 |
+
|
| 288 |
+
print 'publisher:'
|
| 289 |
+
for lang, publisher in desc.publisher.items():
|
| 290 |
+
print ' [%s] %s (%s)' % (lang,
|
| 291 |
+
publisher['name'],
|
| 292 |
+
publisher['url'])
|
| 293 |
+
if desc.license:
|
| 294 |
+
print 'license: accept-by', desc.license_accept_by
|
| 295 |
+
for lang, url in desc.license.items():
|
| 296 |
+
if not root_stg or resolve_path(root_stg, url):
|
| 297 |
+
state = ''
|
| 298 |
+
else:
|
| 299 |
+
state = ' -- MISSING'
|
| 300 |
+
print ' [%s] %s%s' % (lang, url, state)
|
| 301 |
+
|
| 302 |
+
if desc.oo_min_version:
|
| 303 |
+
print 'dependencies:'
|
| 304 |
+
print ' LibreOffice minimal version:', desc.oo_min_version
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def init_main():
|
| 308 |
+
doc = '''Usage: oxt-desc-init [options] <desc-file>
|
| 309 |
+
|
| 310 |
+
--help Print this screen.
|
| 311 |
+
'''
|
| 312 |
+
|
| 313 |
+
from docopt import docopt
|
| 314 |
+
args = docopt(doc)
|
| 315 |
+
logging.basicConfig(level=logging.INFO)
|
| 316 |
+
|
| 317 |
+
description = Description(identifier='tld.example',
|
| 318 |
+
version='0.1',
|
| 319 |
+
display_name=dict(en='Example extension'),
|
| 320 |
+
publisher=dict(en=dict(name='Publisher Name',
|
| 321 |
+
url='http://example.tld')),
|
| 322 |
+
license=dict(url=dict(en='COPYING')),
|
| 323 |
+
description=dict(en='description/en.txt'))
|
| 324 |
+
with file(args['<desc-file>'], 'w') as f:
|
| 325 |
+
description.write(f)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def show_main():
|
| 329 |
+
doc = '''Usage: oxt-desc-show [options] <desc-file>
|
| 330 |
+
|
| 331 |
+
--help Show this screen.
|
| 332 |
+
'''
|
| 333 |
+
from docopt import docopt
|
| 334 |
+
args = docopt(doc)
|
| 335 |
+
logging.basicConfig(level=logging.INFO)
|
| 336 |
+
|
| 337 |
+
with file(args['<desc-file>']) as f:
|
| 338 |
+
desc = Description.parse(f)
|
| 339 |
+
|
| 340 |
+
print_human_readable(desc)
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
def version_main():
|
| 344 |
+
doc = '''Usage: oxt-desc-version [options] <desc-file> [<new-version>]
|
| 345 |
+
|
| 346 |
+
--help Show this screen.
|
| 347 |
+
'''
|
| 348 |
+
from docopt import docopt
|
| 349 |
+
args = docopt(doc)
|
| 350 |
+
logging.basicConfig(level=logging.INFO)
|
| 351 |
+
|
| 352 |
+
with file(args['<desc-file>'], 'r') as f:
|
| 353 |
+
desc = Description.parse(f)
|
| 354 |
+
|
| 355 |
+
new_version = args['<new-version>']
|
| 356 |
+
if new_version is not None:
|
| 357 |
+
logger.info('old: %s', desc.version)
|
| 358 |
+
desc.version = new_version
|
| 359 |
+
logger.info('new: %s', desc.version)
|
| 360 |
+
with file(args['<desc-file>'], 'w') as f:
|
| 361 |
+
desc.write(f)
|
| 362 |
+
else:
|
| 363 |
+
print desc.version
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
def ls_main():
|
| 367 |
+
doc = '''Usage: oxt-desc-ls [options] <desc-file>
|
| 368 |
+
|
| 369 |
+
--help Show this screen.
|
| 370 |
+
'''
|
| 371 |
+
from docopt import docopt
|
| 372 |
+
args = docopt(doc)
|
| 373 |
+
logging.basicConfig(level=logging.INFO)
|
| 374 |
+
|
| 375 |
+
with file(args['<desc-file>']) as f:
|
| 376 |
+
desc = Description.parse(f)
|
| 377 |
+
|
| 378 |
+
for path in desc.required_files():
|
| 379 |
+
print path
|