improve usage of eval/exec; version --> 3.5.32
authorrobin
Thu, 24 Oct 2019 16:07:15 +0100
changeset 4551 d357e2acc856
parent 4550 80dd9e83dad9
child 4552 87794930cfad
improve usage of eval/exec; version --> 3.5.32
CHANGES.md
src/reportlab/__init__.py
src/reportlab/graphics/barcode/test.py
src/reportlab/graphics/renderPDF.py
src/reportlab/graphics/renderPS.py
src/reportlab/graphics/renderSVG.py
src/reportlab/graphics/testshapes.py
src/reportlab/lib/colors.py
src/reportlab/lib/pdfencrypt.py
src/reportlab/lib/rl_accel.py
src/reportlab/pdfbase/_cidfontdata.py
src/reportlab/platypus/doctemplate.py
src/reportlab/platypus/flowables.py
src/reportlab/platypus/tableofcontents.py
--- a/CHANGES.md	Thu Oct 24 15:53:59 2019 +0100
+++ b/CHANGES.md	Thu Oct 24 16:07:15 2019 +0100
@@ -11,6 +11,13 @@
 The contributors lists are in no order and apologies to those accidentally not
 mentioned. If we missed you, please let us know!
 
+RELEASE 3.5.32	24/10/2019
+--------------------------
+	* some chart efficiency changes
+	* use clock in fontFinder contributed by Matěj Cepl @ bitbucket
+	* improve recursive access and do some minor eval/exec fixes
+	* improve use of eval/exec
+
 RELEASE 3.5.31	15/10/2019
 --------------------------
 	* paraparser fix contributed by ravi prakash giri <raviprakashgiri@gmail.com>
--- a/src/reportlab/__init__.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/__init__.py	Thu Oct 24 16:07:15 2019 +0100
@@ -1,9 +1,9 @@
 #Copyright ReportLab Europe Ltd. 2000-2018
 #see license.txt for license details
 __doc__="""The Reportlab PDF generation library."""
-Version = "3.5.31"
+Version = "3.5.32"
 __version__=Version
-__date__='20191019'
+__date__='20191024'
 
 import sys, os
 
--- a/src/reportlab/graphics/barcode/test.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/graphics/barcode/test.py	Thu Oct 24 16:07:15 2019 +0100
@@ -2,6 +2,7 @@
 
 import os, sys, time
 
+from reportlab import Version as __RL_Version__
 from reportlab.graphics.barcode.common import *
 from reportlab.graphics.barcode.code39 import *
 from reportlab.graphics.barcode.code93 import *
@@ -148,23 +149,14 @@
     styleH2 = styles['Heading2']
     story = []
 
-    story.append(Paragraph('ReportLab Barcode Test Suite - full output', styleH))
-    story.append(Paragraph('Generated on %s' % time.ctime(time.time()), styleN))
+    story.append(Paragraph('ReportLab %s Barcode Test Suite - full output' % __RL_Version__,styleH))
+    story.append(Paragraph('Generated at %s' % time.ctime(time.time()), styleN))
 
-    story.append(Paragraph('', styleN))
-    story.append(Paragraph('Repository information for this build:', styleN))
-    #see if we can figure out where it was built, if we're running in source
-    if os.path.split(os.getcwd())[-1] == 'barcode' and os.path.isdir('.svn'):
-        #runnning in a filesystem svn copy
-        infoLines = os.popen('svn info').read()
-        story.append(Preformatted(infoLines, styles["Code"]))
-        
     story.append(Paragraph('About this document', styleH2))
     story.append(Paragraph('History and Status', styleH2))
 
     story.append(Paragraph("""
-        This is the test suite and docoumentation for the ReportLab open source barcode API,
-        being re-released as part of the forthcoming ReportLab 2.0 release.
+        This is the test suite and docoumentation for the ReportLab open source barcode API.
         """, styleN))
 
     story.append(Paragraph("""
--- a/src/reportlab/graphics/renderPDF.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/graphics/renderPDF.py	Thu Oct 24 16:07:15 2019 +0100
@@ -349,8 +349,9 @@
     drawings = []
     for funcname in dir(testshapes):
         if funcname[0:10] == 'getDrawing':
-            drawing = eval('testshapes.' + funcname + '()')  #execute it
-            docstring = eval('testshapes.' + funcname + '.__doc__')
+            func = getattr(testshapes,funcname)
+            drawing = func()  #execute it
+            docstring = getattr(func,'__doc__','')
             drawings.append((drawing, docstring))
 
     #print in a loop, with their doc strings
@@ -381,32 +382,6 @@
     if shout or verbose>2:
         print('saved %s' % ascii(fn))
 
-##def testFlowable():
-##    """Makes a platypus document"""
-##    from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
-##    from reportlab.lib.styles import getSampleStyleSheet
-##    styles = getSampleStyleSheet()
-##    styNormal = styles['Normal']
-##
-##    doc = SimpleDocTemplate('test_flowable.pdf')
-##    story = []
-##    story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal))
-##
-##    import testdrawings
-##    drawings = []
-##
-##    for funcname in dir(testdrawings):
-##        if funcname[0:10] == 'getDrawing':
-##            drawing = eval('testdrawings.' + funcname + '()')  #execute it
-##            docstring = eval('testdrawings.' + funcname + '.__doc__')
-##            story.append(Paragraph(docstring, styNormal))
-##            story.append(Spacer(18,18))
-##            story.append(drawing)
-##            story.append(Spacer(36,36))
-##
-##    doc.build(story)
-##    print 'saves test_flowable.pdf'
-
 if __name__=='__main__':
     test(shout=True)
     import sys
--- a/src/reportlab/graphics/renderPS.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/graphics/renderPS.py	Thu Oct 24 16:07:15 2019 +0100
@@ -918,8 +918,9 @@
 
         for funcname in dir(testshapes):
             if funcname[0:10] == 'getDrawing':
-                drawing = eval('testshapes.' + funcname + '()')  #execute it
-                docstring = eval('testshapes.' + funcname + '.__doc__')
+                func = getattr(testshapes,funcname)
+                drawing = func()
+                docstring = getattr(func,'__doc__','')
                 drawings.append((drawing, docstring))
 
         i = 0
--- a/src/reportlab/graphics/renderSVG.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/graphics/renderSVG.py	Thu Oct 24 16:07:15 2019 +0100
@@ -355,7 +355,7 @@
             self.style['stroke-dasharray'] = ', '.join(map(str, ([array, phase])))
         elif isinstance(array,(tuple,list)) and len(array) > 0:
             assert phase >= 0, "phase is a length in user space"
-            self.style['stroke-dasharray'] = ', '.join(map(str, (array+[phase])))
+            self.style['stroke-dasharray'] = ', '.join(map(str, (list(array)+[phase])))
 
     def setStrokeColor(self, color):
         self._strokeColor = color
@@ -933,14 +933,12 @@
     drawings = []
 
     for funcname in dir(testshapes):
-        #if funcname[0:11] == 'getDrawing2':
-        #    print 'hacked to only show drawing 2'
         if funcname[0:10] == 'getDrawing':
-            drawing = eval('testshapes.' + funcname + '()')
-            docstring = eval('testshapes.' + funcname + '.__doc__')
+            func = getattr(testshapes,funcname)
+            drawing = func()
+            docstring = getattr(func,'__doc__','')
             drawings.append((drawing, docstring))
 
-
     i = 0
     for (d, docstring) in drawings:
         filename = os.path.join(outDir,'renderSVG_%d.svg' % i)
--- a/src/reportlab/graphics/testshapes.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/graphics/testshapes.py	Thu Oct 24 16:07:15 2019 +0100
@@ -487,11 +487,14 @@
     return funcNames
 
 def _evalFuncDrawing(name, D, l=None, g=None):
+    if g is None: g = globals()
+    if l is None: l = locals()
+    func = l.get(name,g.get(name,None))
     try:
-        d = eval(name + '()', g or globals(), l or locals())
+        d = func()
     except:
         d = getFailedDrawing(name)
-    D.append((d, eval(name + '.__doc__'), name[3:]))
+    D.append((d, getattr(func,'.__doc__',''), name[3:]))
 
 def getAllTestDrawings(doTTF=1):
     D = []
--- a/src/reportlab/lib/colors.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/lib/colors.py	Thu Oct 24 16:07:15 2019 +0100
@@ -42,8 +42,9 @@
 import math, re, functools
 from reportlab import isPy3, cmp
 from reportlab.lib.rl_accel import fp_str
-from reportlab.lib.utils import asNative, isStr
+from reportlab.lib.utils import asNative, isStr, safer_globals
 import collections
+from ast import literal_eval
 
 class Color:
     """This class is used to represent color.  Components red, green, blue
@@ -769,8 +770,7 @@
     def pcVal(self,v):
         v = v.strip()
         try:
-            c=eval(v[:-1])
-            if not isinstance(c,(float,int)): raise ValueError
+            c=float(v[:-1])
             c=min(100,max(0,c))/100.
         except:
             raise ValueError('bad percentage argument value %r in css color %r' % (v,self.s))
@@ -782,9 +782,8 @@
     def rgbVal(self,v):
         v = v.strip()
         try:
-            c=eval(v[:])
-            if not isinstance(c,(int,float)): raise ValueError
-            if isinstance(c,float) and 0<=c<=1: c *= 255
+            c=float(v)
+            if 0<c<=1: c *= 255
             return int(min(255,max(0,c)))/255.
         except:
             raise ValueError('bad argument value %r in css color %r' % (v,self.s))
@@ -792,16 +791,14 @@
     def hueVal(self,v):
         v = v.strip()
         try:
-            c=eval(v[:])
-            if not isinstance(c,(int,float)): raise ValueError
+            c=float(v)
             return ((c%360+360)%360)/360.
         except:
             raise ValueError('bad hue argument value %r in css color %r' % (v,self.s))
 
     def alphaVal(self,v,c=1,n='alpha'):
         try:
-            a = eval(v.strip())
-            if not isinstance(a,(int,float)): raise ValueError
+            a = float(v)
             return min(c,max(0,a))
         except:
             raise ValueError('bad %s argument value %r in css color %r' % (n,v,self.s))
@@ -864,7 +861,7 @@
             s = arg.lower()
             if s in C: return C[s]
             try:
-                return toColor(eval(arg))
+                return toColor(eval(arg,safer_globals()))
             except:
                 pass
 
--- a/src/reportlab/lib/pdfencrypt.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/lib/pdfencrypt.py	Thu Oct 24 16:07:15 2019 +0100
@@ -4,7 +4,8 @@
 
 """helpers for pdf encryption/decryption"""
 import sys, os, tempfile
-from reportlab.lib.utils import getBytesIO, md5, asBytes, int2Byte, char2int, rawUnicode, rawBytes, isPy3
+from binascii import hexlify, unhexlify
+from reportlab.lib.utils import getBytesIO, md5, asBytes, int2Byte, char2int, rawUnicode, rawBytes, isPy3, asNative
 from reportlab.pdfgen.canvas import Canvas
 from reportlab.pdfbase import pdfutils
 from reportlab.pdfbase.pdfdoc import PDFObject
@@ -309,18 +310,12 @@
 
 def hexText(text):
     "a legitimate way to show strings in PDF"
-    return '<' + ''.join('%02X' % ord(c) for c in rawUnicode(text)) + '>'
+    return '<%s>' % asNative(hexlify(rawBytes(text))).upper()
 
 def unHexText(hexText):
     equalityCheck(hexText[0], '<', 'bad hex text')
     equalityCheck(hexText[-1], '>', 'bad hex text')
-    hexText = hexText[1:-1]
-    out = b''
-    for i in range(int(len(hexText)/2.0)):
-        slice = hexText[i*2: i*2+2]
-        char = int2Byte(eval('0x'+slice))
-        out = out + char
-    return out
+    return unhexlify(hexText[1:-1])
 
 PadString = rawBytes(''.join(chr(int(c, 16)) for c in padding.strip().split()))
 
@@ -735,9 +730,9 @@
                 try:
                     if argv[pos+1] not in known_modes:
                         if thisarg[0] in binaryrequired:
-                            exec(thisarg[1] +' = int(argv[pos+1])')
+                            exec(thisarg[1] +' = int(argv[pos+1])',vars())
                         else:
-                            exec(thisarg[1] +' = argv[pos+1]')
+                            exec(thisarg[1] +' = argv[pos+1]',vars())
                         if verbose:
                             print("%s set to: '%s'." % (thisarg[3], argv[pos+1]))
                         argv.remove(argv[pos+1])
--- a/src/reportlab/lib/rl_accel.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/lib/rl_accel.py	Thu Oct 24 16:07:15 2019 +0100
@@ -21,12 +21,14 @@
 del reportlab
 
 for fn in __all__:
+    D={}
     try:
-        exec('from reportlab.lib._rl_accel import %s as f' % fn)
-        _c_funcs[fn] = f
+        exec('from reportlab.lib._rl_accel import %s as f' % fn,D)
+        _c_funcs[fn] = D['f']
         if testing: _py_funcs[fn] = None
     except ImportError:
         _py_funcs[fn] = None
+    del D
 
 if _py_funcs:
     from reportlab.lib.utils import isBytes, isUnicode, isSeq, isPy3, rawBytes, asNative, asUnicode, asBytes
@@ -334,19 +336,19 @@
 del fn, f, G
 
 if __name__=='__main__':
-    import sys, os
+    import sys, os, subprocess
     for modname in 'reportlab.lib.rl_accel','reportlab.lib._rl_accel':
         for cmd  in (
             #"unicode2T1('abcde fghi . jkl ; mno',fonts)",
             #"unicode2T1(u'abcde fghi . jkl ; mno',fonts)",
-            "_instanceStringWidthU(font,'abcde fghi . jkl ; mno',10)",
-            "_instanceStringWidthU(font,u'abcde fghi . jkl ; mno',10)",
+            "instanceStringWidthT1(font,'abcde fghi . jkl ; mno',10)",
+            "instanceStringWidthT1(font,u'abcde fghi . jkl ; mno',10)",
             ):
             print('%s %s' % (modname,cmd))
             s=';'.join((
                 "from reportlab.pdfbase.pdfmetrics import getFont",
-                "from %s import unicode2T1,_instanceStringWidthU" % modname,
+                "from %s import unicode2T1,instanceStringWidthT1" % modname,
                 "fonts=[getFont('Helvetica')]+getFont('Helvetica').substitutionFonts""",
                 "font=fonts[0]",
                 ))
-            os.system('%s -m timeit -s"%s" "%s"' % (sys.executable,s,cmd))
+            subprocess.check_call([sys.executable,'-mtimeit','-s',s,cmd])
--- a/src/reportlab/pdfbase/_cidfontdata.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/pdfbase/_cidfontdata.py	Thu Oct 24 16:07:15 2019 +0100
@@ -480,4 +480,4 @@
 ##            out.append(word)
 ##        else:
 ##            out.append(word + ',')
-##    return eval(''.join(out))
+##    return eval(''.join(out),{})
--- a/src/reportlab/platypus/doctemplate.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/platypus/doctemplate.py	Thu Oct 24 16:07:15 2019 +0100
@@ -1215,7 +1215,7 @@
         try:
             if lifetime not in self._allowedLifetimes:
                 raise ValueError('bad lifetime %r not in %r'%(lifetime,self._allowedLifetimes))
-            exec(stmt, {},NS)
+            exec(stmt, NS)
         except:
             for k in NS.keys():
                 if k not in K0:
--- a/src/reportlab/platypus/flowables.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/platypus/flowables.py	Thu Oct 24 16:07:15 2019 +0100
@@ -30,7 +30,7 @@
 from reportlab.lib.rl_accel import fp_str
 from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT, TA_JUSTIFY
 from reportlab.lib.styles import _baseFontName
-from reportlab.lib.utils import strTypes
+from reportlab.lib.utils import strTypes, safer_globals
 from reportlab.lib.abag import ABag
 from reportlab.pdfbase import pdfutils
 from reportlab.pdfbase.pdfmetrics import stringWidth
@@ -755,7 +755,7 @@
     def wrap(self, availWidth, availHeight):
         return (0,0)
     def draw(self):
-        exec(self.command, globals(), {'canvas':self.canv})
+        exec(self.command, safer_globals(), {'canvas':self.canv})
 
 def _nullCallable(*args,**kwds):
     pass
--- a/src/reportlab/platypus/tableofcontents.py	Thu Oct 24 15:53:59 2019 +0100
+++ b/src/reportlab/platypus/tableofcontents.py	Thu Oct 24 16:07:15 2019 +0100
@@ -2,7 +2,7 @@
 #see license.txt for license details
 #history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/platypus/tableofcontents.py
 
-__version__='3.3.0'
+__version__='3.5.32'
 __doc__="""Experimental class to generate Tables of Contents easily
 
 This module defines a single TableOfContents() class that can be used to
@@ -48,6 +48,7 @@
 from reportlab.lib.units import cm
 from reportlab.lib.utils import commasplit, escapeOnce, encode_label, decode_label, strTypes, asUnicode, asNative
 from reportlab.lib.styles import ParagraphStyle, _baseFontName
+from reportlab.lib import sequencer as rl_sequencer
 from reportlab.platypus.paragraph import Paragraph
 from reportlab.platypus.doctemplate import IndexingFlowable
 from reportlab.platypus.tables import TableStyle, Table
@@ -55,6 +56,7 @@
 from reportlab.pdfbase.pdfmetrics import stringWidth
 from reportlab.pdfgen import canvas
 import unicodedata
+from ast import literal_eval
 
 def unquote(txt):
     from xml.sax.saxutils import unescape
@@ -239,7 +241,7 @@
         def drawTOCEntryEnd(canvas, kind, label):
             '''Callback to draw dots and page numbers after each entry.'''
             label = label.split(',')
-            page, level, key = int(label[0]), int(label[1]), eval(label[2],{})
+            page, level, key = int(label[0]), int(label[1]), literal_eval(label[2])
             style = self.getLevelStyle(level)
             if self.dotsMinLevel >= 0 and level >= self.dotsMinLevel:
                 dot = ' . '
@@ -305,13 +307,11 @@
         self._flowable = None
         self.setup(**kwargs)
 
-    def getFormatFunc(self,format):
+    def getFormatFunc(self,formatName):
         try:
-            D = {}
-            exec('from reportlab.lib.sequencer import _format_%s as formatFunc' % format, D)
-            return D['formatFunc']
+            return getattr(rl_sequencer,'_format_%s' % formatName)
         except ImportError:
-            raise ValueError('Unknown format %r' % format)
+            raise ValueError('Unknown sequencer format %r' % formatName)
 
     def setup(self, style=None, dot=None, tableStyle=None, headers=True, name=None, format='123', offset=0):
         """