Hello community,

here is the log from the commit of package python-relatorio for 
openSUSE:Factory checked in at 2019-07-31 14:36:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-relatorio (Old)
 and      /work/SRC/openSUSE:Factory/.python-relatorio.new.4126 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-relatorio"

Wed Jul 31 14:36:54 2019 rev:8 rq:720017 version:0.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-relatorio/python-relatorio.changes        
2019-07-08 15:11:04.543348006 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-relatorio.new.4126/python-relatorio.changes  
    2019-07-31 14:36:57.817847138 +0200
@@ -1,0 +2,13 @@
+Wed Jul 31 09:24:57 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Run tests
+- Run fdupes
+
+-------------------------------------------------------------------
+Tue Jul 30 18:41:41 UTC 2019 - Axel Braun <[email protected]>
+
+- version 0.9.0
+  * Support out parameter of render
+  * Write opendocument stream directly to the ZipFile 
+
+-------------------------------------------------------------------

Old:
----
  relatorio-0.8.1.tar.gz

New:
----
  relatorio-0.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-relatorio.spec ++++++
--- /var/tmp/diff_new_pack.sP44MG/_old  2019-07-31 14:36:58.733846620 +0200
+++ /var/tmp/diff_new_pack.sP44MG/_new  2019-07-31 14:36:58.733846620 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-relatorio
 #
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
 # Copyright (c) 2016-2019 Dr. Axel Braun
 #
 # All modifications and additions to the file contributed by third parties
@@ -13,32 +13,31 @@
 # license that conforms to the Open Source Definition (Version 1.9)
 # published by the Open Source Initiative.
 
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
 #
 
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
-%bcond_with tests
 %define         mod_name relatorio
 Name:           python-relatorio
-Version:        0.8.1
+Version:        0.9.0
 Release:        0
 Summary:        Python module to create reports from Python objects
-License:        GPL-3.0+
+License:        GPL-3.0-or-later
 Group:          Productivity/Office/Management
-Url:            https://pypi.python.org/pypi/relatorio
+URL:            https://pypi.python.org/pypi/relatorio
 Source:         
https://pypi.io/packages/source/r/%{mod_name}/%{mod_name}-%{version}.tar.gz
 BuildRequires:  %{python_module Genshi}
 BuildRequires:  %{python_module lxml}
-# It requires different magic for tests https://github.com/ahupp/python-magic
-#BuildRequires:  %{python_module magic}
+BuildRequires:  %{python_module python-magic}
 BuildRequires:  %{python_module setuptools}
+BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-Genshi
 Requires:       python-PyYAML
 Requires:       python-lxml
-Requires:       python-magic
 Requires:       python-pycha
+Requires:       python-python-magic
 BuildArch:      noarch
 %python_subpackages
 
@@ -55,11 +54,10 @@
 
 %install
 %python_install
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
 
-%if %{with tests}
 %check
 %python_exec setup.py test
-%endif
 
 %files %{python_files}
 %license LICENSE

++++++ relatorio-0.8.1.tar.gz -> relatorio-0.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/.drone.yml 
new/relatorio-0.9.0/.drone.yml
--- old/relatorio-0.8.1/.drone.yml      2018-08-18 13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/.drone.yml      2019-07-29 15:52:57.000000000 +0200
@@ -17,8 +17,6 @@
     include:
         - IMAGE: python:2.7
           TOXENV: py27
-        - IMAGE: python:3.4
-          TOXENV: py34
         - IMAGE: python:3.5
           TOXENV: py35
         - IMAGE: python:3.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/.hgtags new/relatorio-0.9.0/.hgtags
--- old/relatorio-0.8.1/.hgtags 2018-09-30 14:43:38.000000000 +0200
+++ new/relatorio-0.9.0/.hgtags 2019-07-29 16:01:09.000000000 +0200
@@ -25,3 +25,4 @@
 8ee1d7515d35918944151d3a44003063e1ab6a18 0.7.1
 0775b131b51d40c2147b1ae104f760698a8d9f9e 0.8.0
 a9a586fec08da03f86ec781472a981949d4c455a 0.8.1
+a0cf6c5a86e3eaf125a96f942f64114b326dba3b 0.9.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/CHANGES new/relatorio-0.9.0/CHANGES
--- old/relatorio-0.8.1/CHANGES 2018-09-30 14:42:26.000000000 +0200
+++ new/relatorio-0.9.0/CHANGES 2019-07-29 15:55:56.000000000 +0200
@@ -1,3 +1,7 @@
+0.9.0 - 20190729
+* Support out parameter of render
+* Write opendocument stream directly to the ZipFile
+
 0.8.1 - 20180930
 * Add support for Python 3.7
 * Escape invalid XML characters
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/PKG-INFO new/relatorio-0.9.0/PKG-INFO
--- old/relatorio-0.8.1/PKG-INFO        2018-09-30 14:44:06.000000000 +0200
+++ new/relatorio-0.9.0/PKG-INFO        2019-07-29 16:02:35.000000000 +0200
@@ -1,12 +1,13 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: relatorio
-Version: 0.8.1
+Version: 0.9.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.tryton.org/
-Author: Cedric Krier
-Author-email: [email protected]
+Author: Nicolas Evrard
+Author-email: [email protected]
+Maintainer: Cedric Krier
+Maintainer-email: [email protected]
 License: GPL License
-Description-Content-Type: UNKNOWN
 Description: Relatorio
         =========
         
@@ -31,3 +32,5 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Text Processing
+Provides-Extra: fodt
+Provides-Extra: chart
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/doc/conf.py 
new/relatorio-0.9.0/doc/conf.py
--- old/relatorio-0.8.1/doc/conf.py     2018-08-18 13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/doc/conf.py     2019-07-29 15:57:29.000000000 +0200
@@ -48,9 +48,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '0.8'
+version = '0.9'
 # The full version, including alpha/beta/rc tags.
-release = '0.8.1'
+release = '0.9.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
Binary files old/relatorio-0.8.1/examples/big.odt and 
new/relatorio-0.9.0/examples/big.odt differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/examples/demo_context.py 
new/relatorio-0.9.0/examples/demo_context.py
--- old/relatorio-0.8.1/examples/demo_context.py        2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/examples/demo_context.py        2018-11-09 
16:41:53.000000000 +0100
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from os.path import abspath, join, dirname
 from relatorio import Report
 
@@ -6,9 +7,9 @@
 
 # PDF
 if __name__ == '__main__':
-    print "generating output_basic.pdf... ",
+    print("generating output_basic.pdf... ", end='')
     report = Report(abspath(join(dirname(__file__), 'basic.tex')),
         'application/pdf')
     content = report(o=inv).render().getvalue()
     open(join(dirname(__file__), 'output_basic.pdf'), 'wb').write(content)
-    print "done"
+    print("done")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/examples/demo_odf.py 
new/relatorio-0.9.0/examples/demo_odf.py
--- old/relatorio-0.8.1/examples/demo_odf.py    2018-08-18 13:27:26.000000000 
+0200
+++ new/relatorio-0.9.0/examples/demo_odf.py    2018-11-09 22:48:09.000000000 
+0100
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from os.path import abspath, join, dirname
 from relatorio import Report
 from relatorio.templates import opendocument
@@ -12,21 +13,21 @@
 if __name__ == '__main__':
     pwd = dirname(__file__)
     # ODT
-    print "generating output_basic.odt... ",
+    print("generating output_basic.odt... ", end='')
     report = Report(abspath(join(dirname(__file__), 'basic.odt')), ODT_MIME)
     content = report(o=inv).render().getvalue()
     open(join(pwd, 'output_basic.odt'), 'wb').write(content)
-    print "done"
+    print("done")
 
     # we could also use an opendocument template directly
-    print "generating output_template_basic.odt... ",
+    print("generating output_template_basic.odt... ", end='')
     template = opendocument.Template(source='',
         filepath=abspath(join(pwd, 'basic.odt')))
     content = template.generate(o=inv).render().getvalue()
     open(join(pwd, 'output_template_basic.odt'), 'wb').write(content)
-    print "done"
+    print("done")
 
-    print "generating output_complicated.odt... ",
+    print("generating output_complicated.odt... ", end='')
     # Add a chart to the invoice
     inv['chart'] = (
         Report(abspath(join(pwd, 'pie_chart')), 'image/png'), 'image/png')
@@ -34,35 +35,41 @@
     try:
         content = report(o=inv).render().getvalue()
     except NotImplementedError:
-        print "skipped"
+        print("skipped")
     else:
         open(join(pwd, 'output_complicated.odt'), 'wb').write(content)
-        print "done"
+        print("done")
 
-    print "generating output_columns.odt... ",
+    print("generating output_columns.odt... ", end='')
     report = Report(abspath(join(pwd, 'columns.odt')), ODT_MIME)
     lst = [[], ['i'], ['a', 'b'], [1, 2, 3], ['I', 'II', 'III', 'IV']]
     titles = ['first', 'second', 'third', 'fourth']
     content = report(titles=titles, lst=lst).render().getvalue()
     open(join(pwd, 'output_columns.odt'), 'wb').write(content)
-    print "done"
+    print("done")
 
     # ODS
-    print "generating output_pivot.ods... ",
+    print("generating output_pivot.ods... ", end='')
     report = Report(abspath(join(pwd, 'pivot.ods')), ODS_MIME)
     content = report(o=inv).render().getvalue()
     open(join(pwd, 'output_pivot.ods'), 'wb').write(content)
-    print "done"
+    print("done")
 
-    print "generating output_sheets.ods... ",
+    print("generating output_sheets.ods... ", end='')
     report = Report(abspath(join(pwd, 'demo_sheets.ods')), ODS_MIME)
     content = report(lst=lst).render().getvalue()
     open(join(pwd, 'output_sheets.ods'), 'wb').write(content)
-    print "done"
+    print("done")
 
     # ODP
-    print "generating output_presentation.odp... ",
+    print("generating output_presentation.odp... ", end='')
     report = Report(abspath(join(pwd, 'presentation.odp')), ODP_MIME)
     content = report(o=inv).render().getvalue()
     open(join(pwd, 'output_presentation.odp'), 'wb').write(content)
-    print "done"
+    print("done")
+
+    # Big document
+    print("generating output_big.odt... ", end='')
+    report = Report(abspath(join(pwd, 'big.odt')), ODT_MIME)
+    content = report().render(out=open(join(pwd, 'output_big.odt'), 'wb'))
+    print("done")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/examples/demo_repository.py 
new/relatorio-0.9.0/examples/demo_repository.py
--- old/relatorio-0.8.1/examples/demo_repository.py     2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/examples/demo_repository.py     2018-11-09 
16:41:53.000000000 +0100
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import relatorio
 from common import Invoice, inv
 from os.path import join, dirname
@@ -25,8 +26,8 @@
                              ('pivot', '.ods'),
                              ('presentation', '.odp')):
         filename = 'output_%s%s' % (report_name, ext)
-        print "generating '%s'..." % filename,
+        print("generating '%s'..." % filename, end='')
         report, mimetype, desc = repository.by_id(Invoice, report_name)
         data = report(o=inv).render().getvalue()
         open(join(dirname(__file__), filename), 'wb').write(data)
-        print "done"
+        print("done")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/__init__.py 
new/relatorio-0.9.0/relatorio/__init__.py
--- old/relatorio-0.8.1/relatorio/__init__.py   2018-08-18 13:27:26.000000000 
+0200
+++ new/relatorio-0.9.0/relatorio/__init__.py   2019-07-29 15:56:20.000000000 
+0200
@@ -12,5 +12,5 @@
 from .reporting import MIMETemplateLoader, ReportRepository, Report
 from . import templates
 
-__version__ = '0.8.1'
+__version__ = '0.9.0'
 __all__ = ['MIMETemplateLoader', 'ReportRepository', 'Report', 'templates']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/templates/base.py 
new/relatorio-0.9.0/relatorio/templates/base.py
--- old/relatorio-0.8.1/relatorio/templates/base.py     2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/relatorio/templates/base.py     2018-11-09 
22:48:09.000000000 +0100
@@ -31,7 +31,8 @@
 
     def render(self, method=None, encoding='utf-8', out=None, **kwargs):
         "calls the serializer to render the template"
-        return self.serializer(self.events)
+        return self.serializer(
+            self.events, method=method, encoding=encoding, out=out)
 
     def serialize(self, method='xml', **kwargs):
         "generates the bitstream corresponding to the template"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/templates/chart.py 
new/relatorio-0.9.0/relatorio/templates/chart.py
--- old/relatorio-0.8.1/relatorio/templates/chart.py    2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/relatorio/templates/chart.py    2018-11-09 
22:48:09.000000000 +0100
@@ -68,10 +68,13 @@
     def __init__(self):
         self.text_serializer = genshi.output.TextSerializer()
 
-    def __call__(self, stream):
+    def __call__(self, stream, method=None, encoding='utf-8', out=None):
         if not PYCHA_TYPE:
             raise NotImplementedError
-        result = BytesIO()
+        if out is None:
+            result = BytesIO()
+        else:
+            result = out
         yml = StringIO(_encode(self.text_serializer(stream)))
         chart_yaml = yaml.load(yml.read())
         chart_info = chart_yaml['chart']
@@ -95,6 +98,8 @@
         elif chart_type == 'svg':
             surface.finish()
 
-        return result
+        if out is None:
+            return result
+
 
 MIMETemplateLoader.add_factory('chart', Template, Template.id_function)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/templates/opendocument.py 
new/relatorio-0.9.0/relatorio/templates/opendocument.py
--- old/relatorio-0.8.1/relatorio/templates/opendocument.py     2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/relatorio/templates/opendocument.py     2019-07-29 
16:00:49.000000000 +0200
@@ -28,6 +28,7 @@
 
 import base64
 import mimetypes
+import sys
 import time
 import urllib
 import zipfile
@@ -114,9 +115,8 @@
 class ImageHref:
     "A class used to add images in the odf zipfile"
 
-    def __init__(self, zfile, manifest, context):
-        self.zip = zfile
-        self.manifest = manifest
+    def __init__(self, serializer, context):
+        self.serializer = serializer
         self.context = context.copy()
 
     def __call__(self, expr):
@@ -132,9 +132,7 @@
         name = md5(file_content).hexdigest()
         path = 'Pictures/%s%s' % (
             name, mimetypes.guess_extension(mimetype))
-        if path not in self.zip.namelist():
-            self.zip.writestr(path, file_content)
-            self.manifest.add_file_entry(path, mimetype)
+        self.serializer.add_file(path, file_content, mimetype)
         return {'{http://www.w3.org/1999/xlink}href': path}
 
 
@@ -264,6 +262,7 @@
         self.inner_docs = []
         self.has_col_loop = False
         self._source = None
+        self._files = set()
         super(Template, self).__init__(source, filepath, filename, loader,
                                        encoding, lookup, allow_exec)
 
@@ -312,6 +311,7 @@
 
         parsed = []
         for fpath, fparsed in content_files + styles_files:
+            self._files.add(fpath)
             parsed.append((genshi.core.PI, ('relatorio', fpath), None))
             parsed += fparsed
 
@@ -408,12 +408,11 @@
                            % (self.filepath, expr)
             elif expr != statement.text and statement.tag == text_a:
                 warn_msg = "url and text do not match in %s: %s != %s" \
-                           % (self.filepath, expr,
-                              statement.text.encode('utf-8'))
+                           % (self.filepath, expr, statement.text)
             if warn_msg:
                 if directive is not None and not is_opening:
                     warn_msg += " corresponding to opening tag '%s'" \
-                                % opened_tags[-1].text
+                                % opened_tags[-1][0].text
                 warnings.warn(warn_msg)
 
             if directive in GENSHI_CLOSING_DIRECTIVE:
@@ -822,10 +821,8 @@
 
     def generate(self, *args, **kwargs):
         "creates the RelatorioStream."
-        serializer = OOSerializer(self._source)
-        kwargs['__relatorio_make_href'] = ImageHref(serializer.outzip,
-                                                    serializer.manifest,
-                                                    kwargs)
+        serializer = OOSerializer(self._source, self._files)
+        kwargs['__relatorio_make_href'] = ImageHref(serializer, kwargs)
         kwargs['__relatorio_make_dimension'] = ImageDimension(self.namespaces)
         kwargs['__relatorio_guess_type'] = self._guess_type
         kwargs['__relatorio_escape_invalid_chars'] = escape_xml_invalid_chars
@@ -848,6 +845,7 @@
             col_filter = Transformer('//repeat[namespace-uri()="%s"]'
                                      % RELATORIO_URI)
             col_filter = col_filter.apply(transformation)
+            # Must consume the stream to fill counter
             stream = Stream(list(stream), self.serializer) | col_filter
         return RelatorioStream(stream, serializer)
 
@@ -1041,40 +1039,115 @@
         return val
 
 
-class OOSerializer:
+class _AbstractZipWriteSplitStream(object):
+    def __init__(self, zipfile, chunksize=64):
+        self.zipfile = zipfile
+        self.chunksize = chunksize
 
-    def __init__(self, source):
-        self.inzip = get_zip_file(source)
-        self.manifest = Manifest(self.inzip.read(MANIFEST))
-        self.meta = Meta(self.inzip.read(META))
-        self.new_oo = BytesIO()
-        self.outzip = zipfile.ZipFile(
-            self.new_oo, mode='w', compression=zipfile.ZIP_DEFLATED)
-        self.xml_serializer = genshi.output.XMLSerializer()
+    def open(self, zinfo):
+        raise NotImplementedError
+
+    def close(self):
+        raise NotImplementedError
 
     def __call__(self, stream):
-        files = {}
         for kind, data, pos in stream:
             if kind == genshi.core.PI and data[0] == 'relatorio':
-                stream_for = data[1]
+                self.open(data[1])
                 continue
-            files.setdefault(stream_for, []).append((kind, data, pos))
+            yield kind, data, pos
+        self.close()
+
+    def write(self, data):
+        raise NotImplementedError
+
+
+if sys.version_info >= (3, 6):
+    class _ZipWriteSplitStream(_AbstractZipWriteSplitStream):
+        def __init__(self, *args, **kwargs):
+            super(_ZipWriteSplitStream, self).__init__(*args, **kwargs)
+            self._fp = None
+            self._buffer = []
+            self._zinfo = None
+
+        def open(self, zinfo):
+            if self._fp:
+                self.close()
+            self._zinfo = zinfo
+            self._fp = None
+
+        def close(self):
+            self.flush()
+            self._fp.close()
+            self._zinfo = None
+            self._fp = None
+
+        def write(self, data):
+            self._buffer.append(data)
+            if len(self._buffer) > self.chunksize:
+                self.flush()
+
+        def flush(self):
+            if not self._fp:
+                self._fp = self.zipfile.open(self._zinfo, mode='w')
+            self._fp.write(b''.join(self._buffer))
+            self._buffer.clear()
+else:
+    class _ZipWriteSplitStream(_AbstractZipWriteSplitStream):
+        def __init__(self, *args, **kwargs):
+            super(_ZipWriteSplitStream, self).__init__(*args, **kwargs)
+            self._fp = None
+            self._zinfo = None
+
+        def open(self, zinfo):
+            if self._fp:
+                self.close()
+            self._zinfo = zinfo
+            self._fp = BytesIO()
+
+        def close(self):
+            self._fp.seek(0)
+            self.zipfile.writestr(self._zinfo, self._fp.read())
+            self._zinfo = None
+            self._fp = None
+
+        def write(self, data):
+            self._fp.write(data)
+
 
+class OOSerializer:
+
+    def __init__(self, source, files, chunksize=64):
+        self.inzip = get_zip_file(source)
+        self.manifest = Manifest(self.inzip.read(MANIFEST))
+        self.meta = Meta(self.inzip.read(META))
+        self.xml_serializer = genshi.output.XMLSerializer()
+        self._files = files
+        self.chunksize = chunksize
+        self.outzip = None
+        self._deferred = None
+
+    def __call__(self, stream, method=None, encoding='utf-8', out=None):
+        if out is None:
+            result = BytesIO()
+        else:
+            result = out
+        self.outzip = zipfile.ZipFile(
+            result, mode='w', compression=zipfile.ZIP_DEFLATED)
+        self._deferred = []
+        files = {}
         now = time.localtime()[:6]
         manifest_info = None
         for f_info in self.inzip.infolist():
             if f_info.filename.startswith('ObjectReplacements'):
                 continue
-            elif f_info.filename in files:
-                stream = files[f_info.filename]
+            elif f_info.filename in self._files:
                 # create a new file descriptor, copying some attributes from
                 # the original file
                 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),
-                    encoding='utf-8')
-                self.outzip.writestr(new_info, serialized_stream)
+                files[f_info.filename] = new_info
             elif f_info.filename == MANIFEST:
                 manifest_info = f_info
             elif f_info.filename == META:
@@ -1083,12 +1156,29 @@
                 self.manifest.remove_file_entry(f_info.filename)
             else:
                 self.outzip.writestr(f_info, self.inzip.read(f_info.filename))
+
+        writer = _ZipWriteSplitStream(self.outzip, self.chunksize)
+        output_encode(
+            self.xml_serializer(writer(stream)), encoding=encoding, out=writer)
+
+        for args in self._deferred:
+            self.add_file(*args)
         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
+        if out is None:
+            return result
+
+    def add_file(self, path, content, mimetype):
+        if self.outzip and path not in self.outzip.namelist():
+            try:
+                self.outzip.writestr(path, content)
+                self.manifest.add_file_entry(path, mimetype)
+            except ValueError:
+                self._deferred.append((path, content, mimetype))
+
 
 MIMETemplateLoader.add_factory('oo.org', Template)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/templates/pdf.py 
new/relatorio-0.9.0/relatorio/templates/pdf.py
--- old/relatorio-0.8.1/relatorio/templates/pdf.py      2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/relatorio/templates/pdf.py      2018-11-09 
22:48:09.000000000 +0100
@@ -48,23 +48,30 @@
 class PDFSerializer:
 
     def __init__(self):
-        self.working_dir = tempfile.mkdtemp(prefix='relatorio')
-        self.tex_file = os.path.join(self.working_dir, 'report.tex')
-        self.pdf_file = os.path.join(self.working_dir, 'report.pdf')
         self.text_serializer = genshi.output.TextSerializer()
 
-    def __call__(self, stream):
-        tex_file = open(self.tex_file, 'w')
-        tex_file.write(_encode(self.text_serializer(stream)))
-        tex_file.close()
+    def __call__(self, stream, method=None, encoding='utf-8', out=None):
+        if out is None:
+            result = BytesIO()
+        else:
+            result = out
+        working_dir = tempfile.mkdtemp(prefix='relatorio')
+        tex_file = os.path.join(working_dir, 'report.tex')
+        pdf_file = os.path.join(working_dir, 'report.pdf')
+
+        with open(tex_file, 'w') as fp:
+            fp.write(_encode(self.text_serializer(stream)))
 
         subprocess.check_call([TEXEXEC, '--purge', 'report.tex'],
-                              cwd=self.working_dir)
+                              cwd=working_dir)
+
+        with open(pdf_file, 'r') as fp:
+            result.write(fp.read())
+
+        shutil.rmtree(working_dir, ignore_errors=True)
 
-        pdf = BytesIO()
-        pdf.write(open(self.pdf_file, 'r').read())
+        if out is None:
+            return result
 
-        shutil.rmtree(self.working_dir, ignore_errors=True)
-        return pdf
 
 MIMETemplateLoader.add_factory('pdf', Template)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio/tests/test_odt.py 
new/relatorio-0.9.0/relatorio/tests/test_odt.py
--- old/relatorio-0.8.1/relatorio/tests/test_odt.py     2018-08-18 
13:27:26.000000000 +0200
+++ new/relatorio-0.9.0/relatorio/tests/test_odt.py     2019-07-29 
16:00:49.000000000 +0200
@@ -230,6 +230,49 @@
         self.assertTrue(last_row_node.get("value")
                         .startswith('__relatorio_store_col_count'))
 
+    @unittest.skipIf(
+        not getattr(unittest.TestCase, 'assertWarnsRegex', None),
+        "assertWarns not supported")
+    def test_statement_no_text_warning(self):
+        "Test warning for missing statement text"
+        xml = b'''<xml xmlns:text="urn:text" xmlns:xlink="urn:xlink">
+                    <text:a xlink:href="relatorio://content:foo"></text:a>
+                  </xml>'''
+
+        with self.assertWarnsRegex(
+                UserWarning, r"No statement text in '.*' for 'content:foo'"):
+            self.oot.insert_directives(xml)
+
+    @unittest.skipIf(
+        not getattr(unittest.TestCase, 'assertWarnsRegex', None),
+        "assertWarns not supported")
+    def test_statement_missmatch_text_warning(self):
+        "Test warning for missing statement text"
+        xml = b'''<xml xmlns:text="urn:text" xmlns:xlink="urn:xlink">
+                    <text:a 
xlink:href="relatorio://content:foo">content:bar</text:a>
+                  </xml>'''
+
+        with self.assertWarnsRegex(
+                UserWarning,
+                r"url and text do not match in .*: "
+                "content:foo != content:bar"):
+            self.oot.insert_directives(xml)
+
+    @unittest.skipIf(
+        not getattr(unittest.TestCase, 'assertWarnsRegex', None),
+        "assertWarns not supported")
+    def test_statement_text_warning_closing(self):
+        "Test warning for statement text in closing"
+        xml = b'''<xml xmlns:text="urn:text" xmlns:xlink="urn:xlink">
+                    <text:a xlink:href="relatorio://with foo='test'">with 
foo='test'</text:a>
+                    <text:a xlink:href="relatorio:///with"></text:a>
+                  </xml>'''
+
+        with self.assertWarnsRegex(
+                UserWarning,
+                r".* corresponding to opening tag 'with foo='test'"):
+            self.oot.insert_directives(xml)
+
     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">
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio.egg-info/PKG-INFO 
new/relatorio-0.9.0/relatorio.egg-info/PKG-INFO
--- old/relatorio-0.8.1/relatorio.egg-info/PKG-INFO     2018-09-30 
14:44:06.000000000 +0200
+++ new/relatorio-0.9.0/relatorio.egg-info/PKG-INFO     2019-07-29 
16:02:34.000000000 +0200
@@ -1,12 +1,13 @@
-Metadata-Version: 1.1
+Metadata-Version: 2.1
 Name: relatorio
-Version: 0.8.1
+Version: 0.9.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.tryton.org/
-Author: Cedric Krier
-Author-email: [email protected]
+Author: Nicolas Evrard
+Author-email: [email protected]
+Maintainer: Cedric Krier
+Maintainer-email: [email protected]
 License: GPL License
-Description-Content-Type: UNKNOWN
 Description: Relatorio
         =========
         
@@ -31,3 +32,5 @@
 Classifier: Programming Language :: Python :: 3
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Text Processing
+Provides-Extra: fodt
+Provides-Extra: chart
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/relatorio-0.8.1/relatorio.egg-info/SOURCES.txt 
new/relatorio-0.9.0/relatorio.egg-info/SOURCES.txt
--- old/relatorio-0.8.1/relatorio.egg-info/SOURCES.txt  2018-09-30 
14:44:06.000000000 +0200
+++ new/relatorio-0.9.0/relatorio.egg-info/SOURCES.txt  2019-07-29 
16:02:35.000000000 +0200
@@ -28,6 +28,7 @@
 doc/relatorio_basic.png
 examples/basic.odt
 examples/basic.tex
+examples/big.odt
 examples/bouteille.png
 examples/columns.odt
 examples/common.py


Reply via email to