[tryton-debian-vcs] relatorio branch upstream updated. upstream/0.5.7-1-g5305f6d

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Thu Nov 28 18:46:26 UTC 2013


The following commit has been merged in the upstream branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/relatorio.git;a=commitdiff;h=upstream/0.5.7-1-g5305f6d

commit 5305f6de86a7f60007366c55d31b9b5385f8ebfb
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Thu Nov 28 19:45:55 2013 +0100

    Adding upstream version 0.6.0.

diff --git a/CHANGES b/CHANGES
index c466577..15bec1b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+0.6.0 - 20130810
+ * Add support for Python 3
+ * Allow to pass only source to Template
+
 0.5.7 - 20130126
  * Allow string as bitstream for images
 
diff --git a/MANIFEST.in b/MANIFEST.in
index a72bf8f..9f38fae 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,7 @@
 include AUTHORS
 include CHANGES
 include LICENSE
+include relatorio/tests/*.jpg
+include relatorio/tests/*.odt
+include relatorio/tests/*.png
+include relatorio/tests/templates/*.tmpl
diff --git a/PKG-INFO b/PKG-INFO
index 4ed7bfc..e112e9f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: relatorio
-Version: 0.5.7
+Version: 0.6.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.openhex.org/
 Author: Cedric Krier
@@ -22,6 +22,7 @@ Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: GNU General Public License (GPL)
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Text Processing
diff --git a/relatorio.egg-info/PKG-INFO b/relatorio.egg-info/PKG-INFO
index 4ed7bfc..e112e9f 100644
--- a/relatorio.egg-info/PKG-INFO
+++ b/relatorio.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: relatorio
-Version: 0.5.7
+Version: 0.6.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.openhex.org/
 Author: Cedric Krier
@@ -22,6 +22,7 @@ Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: GNU General Public License (GPL)
 Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Text Processing
diff --git a/relatorio.egg-info/SOURCES.txt b/relatorio.egg-info/SOURCES.txt
index 99038ab..017f677 100644
--- a/relatorio.egg-info/SOURCES.txt
+++ b/relatorio.egg-info/SOURCES.txt
@@ -16,4 +16,15 @@ relatorio/templates/__init__.py
 relatorio/templates/base.py
 relatorio/templates/chart.py
 relatorio/templates/opendocument.py
-relatorio/templates/pdf.py
\ No newline at end of file
+relatorio/templates/pdf.py
+relatorio/tests/__init__.py
+relatorio/tests/egg.jpg
+relatorio/tests/one.jpg
+relatorio/tests/test.odt
+relatorio/tests/test_api.py
+relatorio/tests/test_odt.py
+relatorio/tests/two.png
+relatorio/tests/templates/include.tmpl
+relatorio/tests/templates/other.tmpl
+relatorio/tests/templates/test.tmpl
+relatorio/tests/templates/time.tmpl
\ No newline at end of file
diff --git a/relatorio/__init__.py b/relatorio/__init__.py
index cdd9d54..09888e1 100644
--- a/relatorio/__init__.py
+++ b/relatorio/__init__.py
@@ -12,4 +12,4 @@ and report together, find reports by mimetypes/name/python objects.
 from relatorio.reporting import MIMETemplateLoader, ReportRepository, Report
 import templates
 
-__version__ = '0.5.7'
+__version__ = '0.6.0'
diff --git a/relatorio/templates/base.py b/relatorio/templates/base.py
index 5a3d46c..2b69076 100644
--- a/relatorio/templates/base.py
+++ b/relatorio/templates/base.py
@@ -20,11 +20,6 @@
 
 __metaclass__ = type
 
-try:
-    from cStringIO import OutputType
-except ImportError:
-    from StringIO import StringIO as OutputType
-
 import genshi.core
 from genshi.template import NewTextTemplate, MarkupTemplate
 
@@ -46,12 +41,5 @@ class RelatorioStream(genshi.core.Stream):
         "Support for the bitwise operator"
         return RelatorioStream(self.events | function, self.serializer)
 
-    def __str__(self):
-        val = self.render()
-        if isinstance(val, OutputType):
-            return val.getvalue()
-        else:
-            return val
-
 MIMETemplateLoader.add_factory('text', NewTextTemplate)
 MIMETemplateLoader.add_factory('xml', MarkupTemplate)
diff --git a/relatorio/templates/opendocument.py b/relatorio/templates/opendocument.py
index 4f59972..159eff0 100644
--- a/relatorio/templates/opendocument.py
+++ b/relatorio/templates/opendocument.py
@@ -32,9 +32,12 @@ import time
 import urllib
 import zipfile
 try:
-    from cStringIO import StringIO
+    from io import BytesIO
 except ImportError:
-    from StringIO import StringIO
+    try:
+        from cStringIO import StringIO as BytesIO
+    except ImportError:
+        from StringIO import StringIO as BytesIO
 from copy import deepcopy
 import datetime
 from decimal import Decimal
@@ -130,7 +133,7 @@ class ImageHref:
         elif isinstance(bitstream, ChartTemplate):
             bitstream = bitstream.generate(**self.context).render()
         elif not hasattr(bitstream, 'seek') or not hasattr(bitstream, 'read'):
-            bitstream = StringIO(bitstream)
+            bitstream = BytesIO(bitstream)
         bitstream.seek(0)
         file_content = bitstream.read()
         name = md5(file_content).hexdigest()
@@ -237,6 +240,7 @@ class Template(MarkupTemplate):
         self.namespaces = {}
         self.inner_docs = []
         self.has_col_loop = False
+        self._zip_source = None
         super(Template, self).__init__(source, filepath, filename, loader,
                                        encoding, lookup, allow_exec)
 
@@ -245,7 +249,19 @@ class Template(MarkupTemplate):
 
         It adds genshi directives and finds the inner docs.
         """
-        zf = zipfile.ZipFile(self.filepath)
+        if not self.filepath:
+            if hasattr(source, 'read') and hasattr(source, 'mode'):
+                if 'U' in source.mode:
+                    # TemplateLoader of Genshi <= 0.6 open files with universal
+                    # newlines which is not suitable for zipfile
+                    raise ValueError('filepath is required '
+                        'if source is openned with universal newlines')
+                else:
+                    # source could be closed before generate calls
+                    source = BytesIO(source.read())
+        else:
+            source = self.filepath
+        self._zip_source = zf = zipfile.ZipFile(source)
         content = zf.read('content.xml')
         styles = zf.read('styles.xml')
 
@@ -268,7 +284,6 @@ class Template(MarkupTemplate):
             content_files.append((c_path, c_parsed))
             styles_files.append((s_path, s_parsed))
 
-        zf.close()
         parsed = []
         for fpath, fparsed in content_files + styles_files:
             parsed.append((genshi.core.PI, ('relatorio', fpath), None))
@@ -279,7 +294,7 @@ class Template(MarkupTemplate):
     def insert_directives(self, content):
         """adds the genshi directives, handle the images and the innerdocs.
         """
-        tree = lxml.etree.parse(StringIO(content))
+        tree = lxml.etree.parse(BytesIO(content))
         root = tree.getroot()
 
         # assign default/fake namespaces so that documents do not need to
@@ -306,7 +321,7 @@ class Template(MarkupTemplate):
         self._handle_images(tree)
         self._handle_innerdocs(tree)
         self._escape_values(tree)
-        return StringIO(lxml.etree.tostring(tree))
+        return BytesIO(lxml.etree.tostring(tree))
 
     def _invert_style(self, tree):
         "inverts the text:a and text:span"
@@ -692,7 +707,7 @@ class Template(MarkupTemplate):
 
     def generate(self, *args, **kwargs):
         "creates the RelatorioStream."
-        serializer = OOSerializer(self.filepath)
+        serializer = OOSerializer(self._zip_source)
         kwargs['__relatorio_make_href'] = ImageHref(serializer.outzip,
                                                     serializer.manifest,
                                                     kwargs)
@@ -752,13 +767,17 @@ class DuplicateColumnHeaders(object):
 class Manifest(object):
 
     def __init__(self, content):
-        self.tree = lxml.etree.parse(StringIO(content))
+        self.tree = lxml.etree.parse(BytesIO(content))
         self.root = self.tree.getroot()
         self.namespaces = self.root.nsmap
 
     def __str__(self):
-        return lxml.etree.tostring(self.tree, encoding='UTF-8',
-                                   xml_declaration=True)
+        val = lxml.etree.tostring(self.tree, encoding='UTF-8',
+                                  xml_declaration=True)
+        # In Python 3, val will be bytes
+        if not isinstance(val, str):
+            return str(val, 'utf-8')
+        return val
 
     def add_file_entry(self, path, mimetype=None):
         manifest_namespace = self.namespaces['manifest']
@@ -782,7 +801,7 @@ class Manifest(object):
 class Meta(object):
 
     def __init__(self, content):
-        self.tree = lxml.etree.parse(StringIO(content))
+        self.tree = lxml.etree.parse(BytesIO(content))
         root = self.tree.getroot()
         self.namespaces = root.nsmap
         path = '/office:document-meta/office:meta'
@@ -816,17 +835,21 @@ class Meta(object):
         self.remove('printed-by')
         self.remove('creator', 'dc')
         self.remove('date', 'dc')
-        return lxml.etree.tostring(self.tree, encoding='UTF-8',
-                                   xml_declaration=True)
+        val = lxml.etree.tostring(self.tree, encoding='UTF-8',
+                                  xml_declaration=True)
+        # In Python 3, val will be bytes
+        if not isinstance(val, str):
+            return str(val, 'utf-8')
+        return val
 
 
 class OOSerializer:
 
-    def __init__(self, oo_path):
-        self.inzip = zipfile.ZipFile(oo_path)
+    def __init__(self, inzip):
+        self.inzip = inzip
         self.manifest = Manifest(self.inzip.read(MANIFEST))
         self.meta = Meta(self.inzip.read(META))
-        self.new_oo = StringIO()
+        self.new_oo = BytesIO()
         self.outzip = zipfile.ZipFile(self.new_oo, 'w')
         self.xml_serializer = genshi.output.XMLSerializer()
 
@@ -850,7 +873,8 @@ class OOSerializer:
                 new_info = zipfile.ZipInfo(f_info.filename, now)
                 for attr in ('compress_type', 'flag_bits', 'create_system'):
                     setattr(new_info, attr, getattr(f_info, attr))
-                serialized_stream = output_encode(self.xml_serializer(stream))
+                serialized_stream = output_encode(self.xml_serializer(stream),
+                    encoding='utf-8')
                 self.outzip.writestr(new_info, serialized_stream)
             elif f_info.filename == MANIFEST:
                 manifest_info = f_info
@@ -863,7 +887,6 @@ class OOSerializer:
         self.manifest.remove_file_entry(THUMBNAILS + '/')
         if manifest_info:
             self.outzip.writestr(manifest_info, str(self.manifest))
-        self.inzip.close()
         self.outzip.close()
 
         return self.new_oo
diff --git a/relatorio/tests/__init__.py b/relatorio/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/relatorio/tests/egg.jpg b/relatorio/tests/egg.jpg
new file mode 100644
index 0000000..2700e16
Binary files /dev/null and b/relatorio/tests/egg.jpg differ
diff --git a/relatorio/tests/one.jpg b/relatorio/tests/one.jpg
new file mode 100644
index 0000000..48723f0
Binary files /dev/null and b/relatorio/tests/one.jpg differ
diff --git a/relatorio/tests/templates/include.tmpl b/relatorio/tests/templates/include.tmpl
new file mode 100644
index 0000000..cb46df8
--- /dev/null
+++ b/relatorio/tests/templates/include.tmpl
@@ -0,0 +1 @@
+{% include other.tmpl %}
diff --git a/relatorio/tests/templates/other.tmpl b/relatorio/tests/templates/other.tmpl
new file mode 100644
index 0000000..224a5d0
--- /dev/null
+++ b/relatorio/tests/templates/other.tmpl
@@ -0,0 +1 @@
+Another Hello.
diff --git a/relatorio/tests/templates/test.tmpl b/relatorio/tests/templates/test.tmpl
new file mode 100644
index 0000000..3b06a49
--- /dev/null
+++ b/relatorio/tests/templates/test.tmpl
@@ -0,0 +1 @@
+Hello ${o.name}.
diff --git a/relatorio/tests/templates/time.tmpl b/relatorio/tests/templates/time.tmpl
new file mode 100644
index 0000000..8467d56
--- /dev/null
+++ b/relatorio/tests/templates/time.tmpl
@@ -0,0 +1,2 @@
+Hi ${o.name},
+It's ${time} to ${func(y)} !
diff --git a/relatorio/tests/test.odt b/relatorio/tests/test.odt
new file mode 100644
index 0000000..6c3e196
Binary files /dev/null and b/relatorio/tests/test.odt differ
diff --git a/relatorio/tests/test_api.py b/relatorio/tests/test_api.py
new file mode 100644
index 0000000..11e8993
--- /dev/null
+++ b/relatorio/tests/test_api.py
@@ -0,0 +1,124 @@
+###############################################################################
+#
+# Copyright (c) 2007, 2008 OpenHex SPRL. (http://openhex.com) All Rights
+# Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+
+import os
+from nose.tools import *
+
+from relatorio.reporting import (ReportRepository, Report, MIMETemplateLoader,
+                                 DefaultFactory, _absolute, _guess_type)
+
+
+class StubObject(object):
+
+    def __init__(self, **kwargs):
+        for key, val in kwargs.iteritems():
+            setattr(self, key, val)
+
+
+class TestRepository(object):
+
+    def test_register(self):
+        "Testing the registration"
+        reporting = ReportRepository()
+        reporting.add_report(StubObject, 'text/plain',
+                             os.path.join('templates', 'test.tmpl'),
+                             description='Test report')
+
+        assert_true(StubObject in reporting.classes)
+        assert_true('default' in reporting.classes[StubObject].ids)
+        assert_true('text/plain' in reporting.classes[StubObject].mimetypes)
+
+        report, mime, desc = reporting.classes[StubObject].ids['default']
+        eq_(mime, 'text/plain')
+        eq_(desc, 'Test report')
+        eq_(report.mimetype, 'text/plain')
+        assert_true(report.fpath.endswith(os.path.join('templates',
+                                                       'test.tmpl')))
+
+        report2, name = (reporting.classes[StubObject]
+                         .mimetypes['text/plain'][0])
+        eq_(name, 'default')
+        eq_(report, report2)
+
+    def test_mimeguesser(self):
+        eq_(_guess_type('application/pdf'), 'pdf')
+        eq_(_guess_type('text/plain'), 'text')
+        eq_(_guess_type('text/xhtml'), 'markup')
+        eq_(_guess_type('application/vnd.oasis.opendocument.text'), 'oo.org')
+
+    def abspath_helper(self, path):
+        return _absolute(path)
+
+    def test_absolute(self):
+        "Test the absolute path calculation"
+        eq_("/home/nicoe/python/mock.py",
+            _absolute("/home/nicoe/python/mock.py"))
+
+        our_dir, _ = os.path.split(__file__)
+        # We use this because me go up by two frames
+        new_path = self.abspath_helper(os.path.join('brol', 'toto'))
+        eq_(os.path.join(our_dir, 'brol', 'toto'), new_path)
+
+
+class TestReport(object):
+
+    def setup(self):
+        self.loader = MIMETemplateLoader()
+        our_dir, _ = os.path.split(__file__)
+        self.report = Report(os.path.join(our_dir, 'templates', 'test.tmpl'),
+                             'text/plain', DefaultFactory(), self.loader)
+
+    def test_report(self):
+        "Testing the report generation"
+        a = StubObject(name='OpenHex')
+        eq_(self.report(o=a).render(), 'Hello OpenHex.\n')
+
+    def test_factory(self):
+        "Testing the data factory"
+        class MyFactory:
+            def __call__(self, o, time, y=1):
+                d = dict()
+                d['o'] = o
+                d['y'] = y
+                d['time'] = time
+                d['func'] = lambda x: x + 1
+                return d
+
+        our_dir, _ = os.path.split(__file__)
+        report = Report(os.path.join(our_dir, 'templates', 'time.tmpl'),
+                        'text/plain', MyFactory(), self.loader)
+
+        a = StubObject(name='Foo')
+        eq_(report(o=a, time="One o'clock").render(),
+            "Hi Foo,\nIt's One o'clock to 2 !\n")
+        eq_(report(o=a, time="One o'clock", y=4).render(),
+            "Hi Foo,\nIt's One o'clock to 5 !\n")
+        assert_raises(TypeError, report, a)
+
+
+class TestReportInclude(object):
+
+    def test_include(self):
+        our_dir = os.path.dirname(__file__)
+        template_path = os.path.join(our_dir, 'templates')
+        relative_report = Report(os.path.join(template_path, 'include.tmpl'),
+                                 'text/plain')
+        eq_(relative_report().render(), 'Another Hello.\n\n')
diff --git a/relatorio/tests/test_odt.py b/relatorio/tests/test_odt.py
new file mode 100644
index 0000000..2d982ff
--- /dev/null
+++ b/relatorio/tests/test_odt.py
@@ -0,0 +1,312 @@
+# -*- encoding: utf-8 -*-
+###############################################################################
+#
+# Copyright (c) 2007, 2008 OpenHex SPRL. (http://openhex.com) All Rights
+# Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+
+import os
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+import lxml.etree
+from nose.tools import *
+from genshi.filters import Translator
+from genshi.core import PI
+from genshi.template.eval import UndefinedError
+
+from relatorio.templates.opendocument import Template, GENSHI_EXPR,\
+    GENSHI_URI, RELATORIO_URI
+
+OO_TABLE_NS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
+
+
+def pseudo_gettext(string):
+    catalog = {'Mes collègues sont:': 'My colleagues are:',
+               'Bonjour,': 'Hello,',
+               'Je suis un test de templating en odt.':
+                'I am an odt templating test',
+               'Felix da housecat': u'Félix le chat de la maison',
+               'We sell stuff': u'On vend des choses',
+              }
+    return catalog.get(string, string)
+
+def stream_to_string(stream):
+    # In Python 3, stream will be bytes
+    if not isinstance(stream, str):
+        return str(stream, 'utf-8')
+    return stream
+
+
+class TestOOTemplating(object):
+
+    def setup(self):
+        thisdir = os.path.dirname(__file__)
+        filepath = os.path.join(thisdir, 'test.odt')
+        self.oot = Template(open(filepath, mode='rb'))
+        self.data = {'first_name': u'Trente',
+                     'last_name': u'Møller',
+                     'ville': u'Liège',
+                     'friends': [{'first_name': u'Camille',
+                                  'last_name': u'Salauhpe'},
+                                 {'first_name': u'Mathias',
+                                  'last_name': u'Lechat'}],
+                     'hobbies': [u'Music', u'Dancing', u'DJing'],
+                     'animals': [u'Felix da housecat', u'Dog eat Dog'],
+                     'images': [(open(os.path.join(thisdir, 'one.jpg'), 'rb'),
+                                 'image/jpeg'),
+                                (open(os.path.join(thisdir, 'two.png'), 'rb'),
+                                 'image/png')],
+                     'oeuf': open(os.path.join(thisdir, 'egg.jpg'), 'rb'),
+                     'footer': u'We sell stuff'}
+
+    def test_init(self):
+        "Testing the correct handling of the styles.xml and content.xml files"
+        ok_(isinstance(self.oot.stream, list))
+        eq_(self.oot.stream[0], (PI, ('relatorio', 'content.xml'), None))
+        ok_((PI, ('relatorio', 'content.xml'), None) in self.oot.stream)
+
+    def test_directives(self):
+        "Testing the directives interpolation"
+        xml = b'''<xml xmlns:text="urn:text" xmlns:xlink="urn:xlink">
+                    <text:a xlink:href="relatorio://foo">foo</text:a>
+                 </xml>'''
+        interpolated = self.oot.insert_directives(xml)
+        root_interpolated = lxml.etree.parse(interpolated).getroot()
+        child = root_interpolated[0]
+        eq_(child.get('{http://genshi.edgewall.org/}replace'), 'foo')
+
+    def test_column_looping(self):
+        xml = b'''
+<table:table
+    xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
+    xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
+    xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    table:name="Tableau1"
+    table:style-name="Tableau1">
+    <table:table-column table:style-name="Tableau1.A"
+                        table:number-columns-repeated="2"/>
+    <table:table-column table:style-name="Tableau1.C"/>
+    <table:table-column table:style-name="Tableau1.A"/>
+    <table:table-column table:style-name="Tableau1.E"/>
+    <table:table-header-rows>
+        <table:table-row table:style-name="Tableau1.1">
+            <table:table-cell table:style-name="Tableau1.A1"
+                              office:value-type="string">
+                <text:p text:style-name="Table_20_Heading">Brol</text:p>
+            </table:table-cell>
+            <table:table-cell table:style-name="Tableau1.A1"
+                              office:value-type="string">
+                <text:p text:style-name="Table_20_Heading">
+                    <text:a xlink:type="simple"
+                            xlink:href="relatorio://for each="title in titles"">for each="title in titles"</text:a>
+                </text:p>
+            </table:table-cell>
+            <table:table-cell table:style-name="Tableau1.A1"
+                              office:value-type="string">
+                <text:p text:style-name="Table_20_Heading">${title}</text:p>
+                <text:p text:style-name="Table_20_Heading"/>
+            </table:table-cell>
+            <table:table-cell table:style-name="Tableau1.A1"
+                              office:value-type="string">
+                <text:p text:style-name="Table_20_Heading">
+                    <text:a xlink:type="simple"
+                            xlink:href="relatorio:///for">/for</text:a>
+                </text:p>
+            </table:table-cell>
+            <table:table-cell table:style-name="Tableau1.E1"
+                              office:value-type="string">
+                <text:p text:style-name="Table_20_Heading">Truc</text:p>
+            </table:table-cell>
+        </table:table-row>
+    </table:table-header-rows>
+    <table:table-row>
+        <table:table-cell table:style-name="Tableau1.A2"
+                          table:number-columns-spanned="5"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">
+                <text:a xlink:type="simple"
+                        xlink:href="relatorio://for%20each=%22items%20in%20lst%22">for each="items in lst"</text:a>
+            </text:p>
+        </table:table-cell>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+    </table:table-row>
+    <table:table-row>
+        <table:table-cell table:style-name="Tableau1.A3"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">Brol</text:p>
+        </table:table-cell>
+        <table:table-cell table:style-name="Tableau1.A3"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">
+                <text:a xlink:type="simple"
+                        xlink:href="relatorio://for%20each=%22item%20in%20items%22">for each="item in items"</text:a>
+            </text:p>
+        </table:table-cell>
+        <table:table-cell table:style-name="Tableau1.A3"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">${item}</text:p>
+            <text:p text:style-name="Table_20_Contents"/>
+        </table:table-cell>
+        <table:table-cell table:style-name="Tableau1.A3"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">
+                <text:a xlink:type="simple"
+                        xlink:href="relatorio:///for">/for</text:a>
+            </text:p>
+        </table:table-cell>
+        <table:table-cell table:style-name="Tableau1.A2"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">Truc</text:p>
+        </table:table-cell>
+    </table:table-row>
+    <table:table-row>
+        <table:table-cell table:style-name="Tableau1.A2"
+                          table:number-columns-spanned="5"
+                          office:value-type="string">
+            <text:p text:style-name="Table_20_Contents">
+                <text:a xlink:type="simple"
+                        xlink:href="relatorio:///for">/for</text:a>
+            </text:p>
+        </table:table-cell>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+        <table:covered-table-cell/>
+    </table:table-row>
+</table:table>'''
+        interpolated = self.oot.insert_directives(xml)
+        root = lxml.etree.parse(interpolated).getroot()
+        child2 = root[1]
+        eq_(child2.tag, "{%s}repeat" % RELATORIO_URI)
+        eq_(child2.get("closing"), "3")
+        eq_(child2.get("opening"), "1")
+        eq_(len(child2), 1)
+        child4 = root[3]
+        eq_(child4.tag, "{%s}table-header-rows" % OO_TABLE_NS)
+        row1 = child4[0]
+        ok_(row1.get("{%s}attrs" % GENSHI_URI)
+                .startswith('__relatorio_reset_col_count'))
+        eq_(len(row1), 4)
+        loop = row1[1]
+        eq_(loop.tag, "{%s}for" % GENSHI_URI)
+        cell = loop[0]
+        ok_(cell.get("{%s}attrs" % GENSHI_URI)
+                .startswith('__relatorio_inc_col_count'))
+        last_row_node = row1[3]
+        eq_(last_row_node.tag, "{%s}replace" % GENSHI_URI)
+        ok_(last_row_node.get("value")
+                         .startswith('__relatorio_store_col_count'))
+
+    def test_text_outside_p(self):
+        "Testing that the tail text of a directive node is handled properly"
+        xml = b'''<xml xmlns:text="urn:text" xmlns:xlink="urn:xlink">
+                    <text:a xlink:href="relatorio://if%20test=%22True%22">if test="True"</text:a>
+                    xxx
+                    <text:p text:style-name="other">yyy</text:p>
+                    zzz
+                    <text:a xlink:href="relatorio:///if">/if</text:a>
+                    aaa
+                 </xml>'''
+        interpolated = self.oot.insert_directives(xml)
+        root_interpolated = lxml.etree.parse(interpolated).getroot()
+        child = root_interpolated[0]
+        eq_(child.tag, '{http://genshi.edgewall.org/}if')
+        eq_(child.text.strip(), 'xxx')
+        eq_(child.tail.strip(), 'aaa')
+
+    def test_styles(self):
+        "Testing that styles get rendered"
+        stream = self.oot.generate(**self.data)
+        rendered = stream_to_string(stream.events.render(encoding='utf-8'))
+        ok_('We sell stuff' in rendered)
+
+        dico = self.data.copy()
+        del dico['footer']
+        stream = self.oot.generate(**dico)
+        assert_raises(UndefinedError,
+            lambda: stream.events.render(encoding='utf-8'))
+
+    def test_generate(self):
+        "Testing that content get rendered"
+        stream = self.oot.generate(**self.data)
+        rendered = stream_to_string(stream.events.render(encoding='utf-8'))
+        ok_('Bonjour,' in rendered)
+        ok_('Trente' in rendered)
+        ok_('Møller' in rendered)
+        ok_('Dog eat Dog' in rendered)
+        ok_('Felix da housecat' in rendered)
+
+    def test_filters(self):
+        "Testing the filters with the Translator filter"
+        stream = self.oot.generate(**self.data)
+        translated = stream.filter(Translator(pseudo_gettext))
+        translated_xml = stream_to_string(
+            translated.events.render(encoding='utf-8'))
+        ok_("Hello," in translated_xml)
+        ok_("I am an odt templating test" in translated_xml)
+        ok_('Felix da housecat' not in translated_xml)
+        ok_('Félix le chat de la maison' in translated_xml)
+        ok_('We sell stuff' not in translated_xml)
+        ok_('On vend des choses' in translated_xml)
+
+    def test_images(self):
+        "Testing the image replacement directive"
+        stream = self.oot.generate(**self.data)
+        rendered = stream_to_string(stream.events.render(encoding='utf-8'))
+        styles_idx = rendered.find('<?relatorio styles.xml?>')
+        tree = lxml.etree.parse(StringIO(rendered[25:styles_idx]))
+        root = tree.getroot()
+        images = root.xpath('//draw:frame', namespaces=self.oot.namespaces)
+        eq_(len(images), 3)
+        eq_(images[0].get('{%s}name' % self.oot.namespaces['draw']), "")
+        eq_(images[1].get('{%s}name' % self.oot.namespaces['draw']), '')
+        eq_(images[1].get('{%s}width' % self.oot.namespaces['svg']),
+            '1.732cm')
+        eq_(images[1].get('{%s}height' % self.oot.namespaces['svg']),
+            '1.513cm')
+        eq_(images[2].get('{%s}width' % self.oot.namespaces['svg']),
+            '1.732cm')
+        eq_(images[2].get('{%s}height' % self.oot.namespaces['svg']),
+            '1.513cm')
+
+    def test_regexp(self):
+        "Testing the regexp used to find relatorio tags"
+        # a valid expression
+        group = GENSHI_EXPR.match('for each="foo in bar"').groups()
+        eq_(group, (None, 'for', 'each', 'foo in bar'))
+
+        # invalid expr
+        group = GENSHI_EXPR.match('foreach="foo in bar"').groups()
+        eq_(group, (None, None, None, None))
+
+        # valid closing tags
+        group = GENSHI_EXPR.match('/for').groups()
+        eq_(group, ('/', 'for', None, None))
+        group = GENSHI_EXPR.match('/for ').groups()
+        eq_(group, ('/', 'for', None, None))
+
+        # another non matching expr
+        group = GENSHI_EXPR.match('formatLang("en")').groups()
+        eq_(group, (None, None, None, None))
diff --git a/relatorio/tests/two.png b/relatorio/tests/two.png
new file mode 100644
index 0000000..f7d65d5
Binary files /dev/null and b/relatorio/tests/two.png differ
diff --git a/setup.py b/setup.py
index ef10e1a..f342bf6 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ setup(
     url="http://relatorio.openhex.org/",
     author="Nicolas Evrard",
     author_email="nicoe at openhex.org",
-    maintainer=u"Cedric Krier",
+    maintainer="Cedric Krier",
     maintainer_email="cedric.krier at b2ck.com",
     description="A templating library able to output odt and pdf files",
     long_description="""
@@ -28,7 +28,10 @@ and report together, find reports by mimetypes/name/python objects.
     """,
     license="GPL License",
     version=get_version(),
-    packages=find_packages(exclude=['relatorio.tests', 'examples']),
+    packages=find_packages(exclude=['examples']),
+    package_data={
+        'relatorio.tests': ['*.jpg', '*.odt', '*.png', 'templates/*.tmpl'],
+        },
     install_requires=[
         "Genshi >= 0.5",
         "lxml >= 2.0"
@@ -38,9 +41,13 @@ and report together, find reports by mimetypes/name/python objects.
         "Intended Audience :: Developers",
         "License :: OSI Approved :: GNU General Public License (GPL)",
         "Operating System :: OS Independent",
-        "Programming Language :: Python",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
         "Topic :: Software Development :: Libraries :: Python Modules",
         "Topic :: Text Processing",
     ],
     test_suite="nose.collector",
-    )
+    tests_require=[
+        "nose",
+    ],
+    use_2to3=True)
-- 
relatorio



More information about the tryton-debian-vcs mailing list