revert to old style recursiveGetAttr; version --> 3.5.39
authorrobin
Wed, 26 Feb 2020 14:53:45 +0000
changeset 4577 0ee00d55c105
parent 4576 95860bf529ca
child 4578 325c308a1c36
revert to old style recursiveGetAttr; version --> 3.5.39
CHANGES.md
src/reportlab/__init__.py
src/reportlab/lib/utils.py
tests/test_lib_utils.py
--- a/CHANGES.md	Wed Feb 26 14:52:59 2020 +0000
+++ b/CHANGES.md	Wed Feb 26 14:53:45 2020 +0000
@@ -11,6 +11,12 @@
 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.39	26/02/2020
+--------------------------
+	* allow selection of ttf subfonts by PS name
+	* revert to old style recursiveGetAttr
+	* raise error for problematic Canvas.setDash reported by Mike Carter from sitemorse
+
 RELEASE 3.5.38	14/02/2020
 --------------------------
 	* bug fix for normalDate monthnames; bump travis; version-->3.5.38
--- a/src/reportlab/__init__.py	Wed Feb 26 14:52:59 2020 +0000
+++ b/src/reportlab/__init__.py	Wed Feb 26 14:53:45 2020 +0000
@@ -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.38"
+Version = "3.5.39"
 __version__=Version
-__date__='20200207'
+__date__='20200226'
 
 import sys, os
 
--- a/src/reportlab/lib/utils.py	Wed Feb 26 14:52:59 2020 +0000
+++ b/src/reportlab/lib/utils.py	Wed Feb 26 14:53:45 2020 +0000
@@ -1508,80 +1508,33 @@
             a = ' '.join(a)
         return a
 
-###############################################################
-#the following code has been (thanks to MIT license)
-#freely adapted from https://github.com/frmdstryr/magicattr
-#the function names are changed to avoid clasing with the actual
-#magicattr names to prevent confusion should others be using that
-#as well as ReportLab
-from functools import reduce as functools_reduce
-
-#: Types of AST nodes that are used
-_raccess_ast_types = (ast.Name, ast.Attribute, ast.Subscript, ast.Call)
-
-def recursiveGetAttr(ob, a):
-    return functools_reduce(_raccess_getattr, _raccess_get_nodes(a), ob)
+def recursiveGetAttr(obj, name, g=None):
+    "Can call down into e.g. object1.object2[4].attr"
+    if not isStr(name): raise TypeError('invalid reursive acess using %r' % name)
+    name = asNative(name)
+    name = name.strip()
+    if not name: raise ValueError('empty recursive access')
+    dot = '.' if name and name[0] not in '[.(' else ''
+    return rl_safe_eval('obj%s%s'%(dot,name), g={}, l=dict(obj=obj))
 
-def recursiveSetAttr(ob, a, val):
-    ob, attr_or_key, is_subscript = raccess_lookup(ob, a)
-    if is_subscript:
-        ob[attr_or_key] = val
-    else:
-        setattr(ob, attr_or_key, val)
-
-def recursiveDelAttr(ob, a):
-    ob, attr_or_key, is_subscript = raccess_lookup(ob, a)
-    if is_subscript:
-        del ob[attr_or_key]
-    else:
-        delattr(ob, attr_or_key)
-
-def raccess_lookup(ob, a):
-    N = tuple(_raccess_get_nodes(a))
-    if len(N) > 1:
-        ob = functools_reduce(_raccess_getattr, N[:-1], ob)
-        n = N[-1]
+def recursiveSetAttr(obj, name, value):
+    "Can call down into e.g. object1.object2[4].attr = value"
+    #get the thing above last.
+    tokens = name.split('.')
+    if len(tokens) == 1:
+        setattr(obj, name, value)
     else:
-        n = N[0]
-    if isinstance(n, ast.Attribute):
-        return ob, n.attr, False
-    elif isinstance(n, ast.Subscript):
-        return ob, _raccess_getitem(n.slice.value), True
-    elif isinstance(n, ast.Name):
-        return ob, n.id, False
-    raise NotImplementedError("access by %s is not supported" % n)
-
-def _raccess_get_nodes(a):
-    if not isStr(a):
-        raise TypeError("Attribute name must be a string not %s" % repr(a))
-    if not isNative(a):
-        a = asNative(a)
-    N = ast.parse(a).body
-    if not N or not isinstance(N[0], ast.Expr):
-        raise ValueError("Invalid expression: %s"%a)
-    return reversed([n for n in ast.walk(N[0])
-                     if isinstance(n, _raccess_ast_types)])
+        most = '.'.join(tokens[:-1])
+        last = tokens[-1]
+        parent = recursiveGetAttr(obj, most)
+        setattr(parent, last, value)
 
-def _raccess_getitem(n):
-    # Handle indexes
-    if isinstance(n, ast.Num):
-        return n.n
-    # Handle string keys
-    elif isinstance(n, ast.Str):
-        return n.s
-    # Handle negative indexes
-    elif (isinstance(n, ast.UnaryOp) and isinstance(n.op, ast.USub)
-          and isinstance(n.operand, ast.Num)):
-        return -n.operand.n
-    raise NotImplementedError("subscripting unsupported for node: %s" % n)
-
-def _raccess_getattr(ob, n):
-    if isinstance(n, ast.Attribute):
-        return getattr(ob, n.attr)
-    elif isinstance(n, ast.Subscript):
-        return ob[_raccess_getitem(n.slice.value)]
-    elif isinstance(n, ast.Name):
-        return getattr(ob, n.id)
-    elif isinstance(n, ast.Call):
-        raise ValueError("Function calls are not allowed.")
-    raise NotImplementedError("unsupported node: %s" % n)
+def recursiveDelAttr(obj, name):
+    tokens = name.split('.')
+    if len(tokens) == 1:
+        delattr(obj, name)
+    else:
+        most = '.'.join(tokens[:-1])
+        last = tokens[-1]
+        parent = recursiveGetAttr(obj, most)
+        delattr(parent, last)
--- a/tests/test_lib_utils.py	Wed Feb 26 14:52:59 2020 +0000
+++ b/tests/test_lib_utils.py	Wed Feb 26 14:53:45 2020 +0000
@@ -278,8 +278,8 @@
         self.assertEqual(recursiveGetAttr(jack, 'friends[-1].settings["themes"][1]') ,'dark')
 
         # Setattr
-        recursiveSetAttr(bob, 'settings["style"]["width"]', 400)
-        self.assertEqual(recursiveGetAttr(bob, 'settings["style"]["width"]') ,400)
+        #recursiveSetAttr(bob, 'settings["style"]["width"]', 400)
+        #self.assertEqual(recursiveGetAttr(bob, 'settings["style"]["width"]') ,400)
 
         # Nested objects
         recursiveSetAttr(bob, 'friends', [jack, jill])
@@ -289,8 +289,8 @@
         self.assertEqual(bob.age ,32)
 
         # Deletion
-        recursiveDelAttr(jill, 'friends[0]')
-        self.assertEqual(len(jill.friends) ,0)
+        #recursiveDelAttr(jill, 'friends[0]')
+        #self.assertEqual(len(jill.friends) ,0)
 
         recursiveDelAttr(jill, 'age')
         assert not hasattr(jill, 'age')
@@ -299,15 +299,15 @@
         assert not hasattr(jack, 'age')
 
         # Unsupported
-        with self.assertRaises(NotImplementedError) as e:
-            recursiveGetAttr(bob, 'friends[0+1]')
+        #with self.assertRaises(NotImplementedError) as e:
+        #   recursiveGetAttr(bob, 'friends[0+1]')
 
         # Nice try, function calls are not allowed
-        with self.assertRaises(ValueError):
-            recursiveGetAttr(bob, 'friends.pop(0)')
+        #with self.assertRaises(ValueError):
+        #   recursiveGetAttr(bob, 'friends.pop(0)')
 
         # Must be an expression
-        with self.assertRaises(ValueError):
+        with self.assertRaises(SyntaxError):
             recursiveGetAttr(bob, 'friends = []')
 
         # Must be an expression