[Checkins] SVN: z3c.rml/trunk/src/z3c/rml/ Implemented a workaround for rendering PDF with

Roger Ineichen roger at projekt01.ch
Sat Aug 11 21:50:07 EDT 2007


Log message for revision 78750:
  Implemented a workaround for rendering PDF with 
  the "not thread safe" ReportLab library.
  
  Implemented a method which renders PDF in a subprocess
  using the sys.path from the parent process. Oh this eggs!
  
  Note: 
  In the test_subprocess, we get rid of the thread 
  un-safe error: ValueError: object named but not registered

Changed:
  U   z3c.rml/trunk/src/z3c/rml/directive.py
  A   z3c.rml/trunk/src/z3c/rml/rml2pdfscript.py
  U   z3c.rml/trunk/src/z3c/rml/tests/test_rml.py
  A   z3c.rml/trunk/src/z3c/rml/tests/test_subprocess.py

-=-
Modified: z3c.rml/trunk/src/z3c/rml/directive.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/directive.py	2007-08-11 19:17:42 UTC (rev 78749)
+++ z3c.rml/trunk/src/z3c/rml/directive.py	2007-08-12 01:50:06 UTC (rev 78750)
@@ -23,6 +23,7 @@
 from lxml import etree
 from z3c.rml import interfaces
 
+logging.raiseExceptions = False
 logger = logging.getLogger("z3c.rml")
 
 

Added: z3c.rml/trunk/src/z3c/rml/rml2pdfscript.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/rml2pdfscript.py	                        (rev 0)
+++ z3c.rml/trunk/src/z3c/rml/rml2pdfscript.py	2007-08-12 01:50:06 UTC (rev 78750)
@@ -0,0 +1,117 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""RML to PDF Converter
+
+$Id: rml2pdf.py 74160 2007-04-15 22:04:24Z srichter $
+"""
+__docformat__ = "reStructuredText"
+
+import subprocess
+import sys
+import os
+
+_fileOpen = None
+
+
+def excecuteSubProcess(xmlInputName, outputFileName, testing=None):
+    # set the sys path given from the parent process
+    sysPath = os.environ['Z3CRMLSYSPATH']
+    for p in sysPath.split(';'):
+        sys.path.insert(0, p)
+
+    # now it come the ugly thing, but we need to hook our test changes into
+    # our subprocess.
+    if testing is not None:
+        
+        # set some globals
+        import z3c.rml.attr
+        import z3c.rml.directive
+        global _fileOpen
+        _fileOpen = z3c.rml.attr.File.open
+        def testOpen(img, filename):
+            # cleanup win paths like:
+            # ....\\input\\file:///D:\\trunk\\...
+            if sys.platform[:3].lower() == "win":
+                filename.replace('/', '\\')
+                if filename.startswith('file:///'):
+                    filename = filename[len('file:///'):]
+            path = os.path.join(os.path.dirname(xmlInputName), filename)
+            return open(path, 'rb')
+        # override some testing stuff for our tests
+        z3c.rml.attr.File.open = testOpen
+        import z3c.rml.tests.module
+        sys.modules['module'] = z3c.rml.tests.module
+        sys.modules['mymodule'] = z3c.rml.tests.module
+
+    # import rml and process the pdf
+    from z3c.rml import rml2pdf
+    rml2pdf.go(xmlInputName, outputFileName)
+
+    if testing is not None:
+        # reset some globals
+        z3c.rml.attr.File.open = _fileOpen
+        del sys.modules['module']
+        del sys.modules['mymodule']
+
+
+def goSubProcess(xmlInputName, outputFileName, testing=False):
+    """Processes PDF rendering in a sub process.
+
+    This method is much slower then the ``go`` method defined in rml2pdf.py
+    because each PDF generation is done in a sub process. But this will make
+    sure, that we do not run into problems. Note, the ReportLab lib is not
+    threadsafe. 
+
+    Use this method from python and it will dispatch the pdf generation 
+    to a subprocess.
+
+    Note: this method does not take care on how much process will started.
+    Probably it's a good idea to use a queue or a global utility which only
+    start a predefined amount of sub processes.
+    """
+    # get the sys path used for this python process
+    env = os.environ
+    sysPath = ';'.join(sys.path)
+    # set the sys path as env var for the new sub process
+    env['Z3CRMLSYSPATH'] = sysPath
+    py = sys.executable
+
+    # setup the cmd
+    program = [py, __file__, 'excecuteSubProcess', xmlInputName, outputFileName]
+    if testing is True:
+        program.append('testing=1')
+    program = " ".join(program)
+
+    # start processing in a sub process, raise exception or return None
+    try:
+        p = subprocess.Popen(program, env=env, stdin=subprocess.PIPE, 
+            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    except Exception, e:
+        raise Exception("Subprocess error: %s" % e)
+
+    # Do we need improve the implementation and to kill subprocess which will 
+    # fail? ri
+    stdout, stderr = p.communicate()
+    error = stderr
+    if error:
+        raise Exception("Subprocess error: %s" % error) 
+
+
+if __name__ == '__main__':
+    if len(sys.argv) == 5:
+        #testing support
+        canvas = excecuteSubProcess(sys.argv[2], sys.argv[3], sys.argv[4])
+    else:
+        canvas = excecuteSubProcess(sys.argv[2], sys.argv[3])
+


Property changes on: z3c.rml/trunk/src/z3c/rml/rml2pdfscript.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.rml/trunk/src/z3c/rml/tests/test_rml.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/test_rml.py	2007-08-11 19:17:42 UTC (rev 78749)
+++ z3c.rml/trunk/src/z3c/rml/tests/test_rml.py	2007-08-12 01:50:06 UTC (rev 78750)
@@ -17,7 +17,7 @@
 """
 import os
 import PIL
-import popen2
+import subprocess
 import unittest
 import sys
 import z3c.rml.tests
@@ -26,6 +26,7 @@
 GS_COMMAND = ('gs -q -sNOPAUSE -sDEVICE=png256 -sOutputFile=%s[Page-%%d].png '
               '%s -c quit')
 
+
 class RMLRenderingTestCase(unittest.TestCase):
 
     def __init__(self, inPath, outPath):
@@ -37,6 +38,12 @@
         # Switch file opener for Image attibute
         self._fileOpen = attr.File.open
         def testOpen(img, filename):
+            # cleanup win paths like:
+            # ....\\input\\file:///D:\\trunk\\...
+            if sys.platform[:3].lower() == "win":
+                filename.replace('/', '\\')
+                if filename.startswith('file:///'):
+                    filename = filename[len('file:///'):]
             path = os.path.join(os.path.dirname(self._inPath), filename)
             return open(path, 'rb')
         attr.File.open = testOpen
@@ -71,13 +78,13 @@
 
     def runTest(self):
         # Convert the base PDF to image(s)
-        status = popen2.Popen3(
-            GS_COMMAND %(self._basePath[:-4], self._basePath), True).wait()
+        status = subprocess.Popen(
+            GS_COMMAND %(self._basePath[:-4], self._basePath)).wait()
         if status:
             return
         # Convert the test PDF to image(s)
-        status = popen2.Popen3(
-            GS_COMMAND %(self._testPath[:-4], self._testPath), True).wait()
+        status = subprocess.Popen(
+            GS_COMMAND %(self._testPath[:-4], self._testPath)).wait()
         if status:
             return
         # Go through all pages and ensure their equality

Added: z3c.rml/trunk/src/z3c/rml/tests/test_subprocess.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/test_subprocess.py	                        (rev 0)
+++ z3c.rml/trunk/src/z3c/rml/tests/test_subprocess.py	2007-08-12 01:50:06 UTC (rev 78750)
@@ -0,0 +1,62 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTLAR PURPOSE.
+#
+##############################################################################
+"""Testing all XML Locale functionality.
+
+$Id: test_rml.py 74170 2007-04-16 08:42:13Z srichter $
+"""
+import os
+import PIL
+import popen2
+import unittest
+import sys
+import z3c.rml.tests
+from z3c.rml import rml2pdfscript
+from z3c.rml import attr
+from z3c.rml.tests.test_rml import ComparePDFTestCase
+from z3c.rml.tests.test_rml import RMLRenderingTestCase
+
+
+class RMLRenderingTestCase(RMLRenderingTestCase):
+
+    def runTest(self):
+        rml2pdfscript.goSubProcess(self._inPath, self._outPath, True)
+
+
+def test_suite():
+   suite = unittest.TestSuite()
+   inputDir = os.path.join(os.path.dirname(z3c.rml.tests.__file__), 'input')
+   outputDir = os.path.join(os.path.dirname(z3c.rml.tests.__file__), 'output')
+   expectDir = os.path.join(os.path.dirname(z3c.rml.tests.__file__), 'expected')
+   for filename in os.listdir(inputDir):
+       if not filename.endswith(".rml"):
+           continue
+       inPath = os.path.join(inputDir, filename)
+       outPath = os.path.join(outputDir, filename[:-4] + '.pdf')
+       expectPath = os.path.join(expectDir, filename[:-4] + '.pdf')
+
+       # ** Test RML to PDF rendering **
+       # Create new type, so that we can get test matching
+       TestCase = type(filename[:-4], (RMLRenderingTestCase,), {})
+       case = TestCase(inPath, outPath)
+       suite.addTest(case)
+
+       # ** Test PDF rendering correctness **
+       TestCase = type('compare-'+filename[:-4], (ComparePDFTestCase,), {})
+       case = TestCase(expectPath, outPath)
+       suite.addTest(case)
+
+   return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')


Property changes on: z3c.rml/trunk/src/z3c/rml/tests/test_subprocess.py
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list