1 # -*- coding: utf-8 -*-
2 ###########################################################################
3 # Copyright (C) 2008 by Andrew Mahone
4 # <andrew.mahone@gmail.com>
6 # Copyright: See COPYING file that comes with this distribution
8 ###########################################################################
9 from __future__
import absolute_import
13 import encodings
.punycode
14 from RestrictedPython
.RCompile
import RExpression
15 from RestrictedPython
.MutatingWalker
import walk
16 from RestrictedPython
.Guards
import safe_builtins
as eval_builtins
17 from string
import maketrans
18 from compiler
import ast
19 from audiomangler
import Config
21 breakre
= re
.compile("\(|\)|\$\$|\$(?P<nosan>/)?(?P<label>[a-z]+)?(?P<paren>\()?|(?P<raw>[rR])?(?P<quote>'|\")|\\\\.")
22 pathseptrans
= unicode(maketrans('/','_')[:48])
23 pathtrans
= unicode(maketrans(r
'/\[]?=+<>;",*|', os
.path
.sep
+ '_' * 13)[:125])
25 eval_builtins
= eval_builtins
.copy()
26 eval_builtins
.update(filter=filter, map=map, max=max, min=min, reduce=reduce, reversed=reversed, slice=slice, sorted=sorted)
27 del eval_builtins
['delattr']
28 del eval_builtins
['setattr']
29 eval_globals
= {'__builtins__':eval_builtins
, '_getattr_':getattr, '_getitem_': lambda x
,y
: x
[y
]}
31 def underscorereplace_errors(e
):
32 return (u
'_' * (e
.end
- e
.start
), e
.end
)
34 codecs
.register_error('underscorereplace', underscorereplace_errors
)
36 def evaluate(item
,cdict
):
37 if isinstance(item
,Expr
):
38 return item
.evaluate(cdict
)
42 class InlineFuncsVisitor
:
43 def __init__(self
, filename
, baseexpr
):
44 self
.filename
= filename
45 self
.baseexpr
= baseexpr
46 def visitCallFunc(self
, node
, *args
):
47 if not hasattr(node
, 'node'):
49 if not isinstance(node
.node
, ast
.Name
):
51 handler
= getattr(self
, '_' + node
.node
.name
, None)
53 return handler(node
, *args
)
56 def _first(self
, node
, *args
):
57 clocals
= ast
.Const(locals)
58 clocals
.lineno
= node
.lineno
59 clocals
= ast
.CallFunc(clocals
,[],None,None)
60 clocals
.lineno
= node
.lineno
62 exp
.lineno
= node
.lineno
63 for item
in node
.args
:
64 if not isinstance(item
, ast
.Const
) or isinstance(item
.value
, basestring
):
65 if isinstance(item
, ast
.Const
):
67 item
= self
.baseexpr(item
, self
.filename
)
68 item
= ast
.Const(item
.evaluate
)
69 item
.lineno
= node
.lineno
70 item
= ast
.CallFunc(item
, [clocals
])
71 item
.lineno
= node
.lineno
72 exp
.nodes
.append(item
)
75 class Expr(RExpression
,object):
76 _globals
= eval_globals
79 def __new__(cls
, source
, filename
="", baseexpr
=None):
80 key
= (cls
, source
, filename
, baseexpr
)
81 if isinstance(source
, basestring
):
82 if key
not in cls
._cache
:
83 cls
._cache
[key
] = object.__new
__(cls
)
84 return cls
._cache
[key
]
85 elif isinstance(source
, ast
.Node
):
86 return object.__new
__(cls
)
87 elif isinstance(source
,cls
):
90 def __init__(self
, source
, filename
="", baseexpr
=None):
91 if hasattr(self
,'_compiled'):
94 self
._baseexpr
= baseexpr
or getattr(self
.__class
__,'_baseexpr', None) or self
.__class
__
95 self
._filename
= filename
96 if not isinstance(source
, ast
.Node
):
97 RExpression
.__init
__(self
, source
, filename
)
98 source
= self
._get
_tree
()
100 if not (isinstance(source
, ast
.Expression
)):
101 source
= ast
.Expression(source
)
102 source
.filename
= filename
103 walk(source
, InlineFuncsVisitor(self
._filename
, self
._baseexpr
))
104 gen
= self
.CodeGeneratorClass(source
)
105 self
._compiled
= gen
.getCode()
108 return hash(self
._compiled
)
111 tree
= RExpression
._get
_tree
(self
)
112 walk(tree
, InlineFuncsVisitor(self
.filename
, self
._baseexpr
))
115 def evaluate(self
, cdict
):
117 return eval(self
._compiled
, self
._globals
, cdict
)
121 class StringExpr(Expr
):
122 def evaluate(self
, cdict
):
123 ret
= super(self
.__class
__,self
).evaluate(cdict
)
128 class SanitizedExpr(Expr
):
129 def evaluate(self
, cdict
):
130 ret
= super(self
.__class
__,self
).evaluate(cdict
)
132 ret
= unicode(ret
).translate(pathseptrans
)
139 clocals
= ast
.Const(locals)
141 clocals
= ast
.CallFunc(clocals
, [], None, None)
143 items
= self
._parse
()
149 if isinstance(item
, Expr
):
150 item
= ast
.Const(item
.evaluate
)
152 item
= ast
.CallFunc(item
, [clocals
])
154 ta
.nodes
.append(item
)
155 te
.nodes
.append(item
)
157 item
= ast
.Const(item
)
159 ta
.nodes
.append(item
)
160 result
= ast
.Const(''.join
)
162 result
= ast
.CallFunc(result
,[ta
],None, None)
164 none
= ast
.Name('None')
166 test
= ast
.Compare(none
, [('in',te
)])
168 result
= ast
.IfExp(test
, none
, result
)
170 result
= ast
.Expression(result
)
172 result
.filename
= self
._filename
180 for m
in breakre
.finditer(self
._source
):
181 # import pdb; pdb.set_trace()
184 if m
.start() > prevend
:
185 cur
.append(self
._source
[prevend
:m
.start()])
190 elif mt
.startswith('$'):
191 if not (mg
['label'] or mg
['paren']):
195 result
.append(''.join(cur
))
198 if mg
['nosan'] or not self
._sanitize
:
199 result
.append(StringExpr(mg
['label'], self
._filename
, self
._baseexpr
))
201 result
.append(SanitizedExpr(mg
['label'], self
._filename
, self
._baseexpr
))
203 if mg
['nosan'] or not self
._sanitize
:
204 cur
.append(StringExpr
)
206 cur
.append(SanitizedExpr
)
208 cur
.append(mg
['label'])
219 state
.append(mg
['quote'])
220 elif mt
.endswith('('):
223 if mg
['quote'] == state
[-1]:
226 result
.append(cur
[0](''.join(cur
[1:]), self
._filename
, self
._baseexpr
))
228 cur
.append(self
._source
[prevend
:])
230 raise SyntaxError('unexpected EOF while parsing',(self
._filename
,1,len(self
._source
),self
._source
))
232 result
.append(''.join(cur
))
235 class SanitizedFormat(Format
):
238 class FileFormat(SanitizedFormat
):
239 _baseexpr
= SanitizedFormat
240 def evaluate(self
, cdict
):
241 ret
= super(self
.__class
__,self
).evaluate(cdict
)
243 ret
= ret
.translate(pathtrans
)
248 def unique(testset
, expr
, evalexpr
): pass
250 __all__
= ['Format','FileFormat','Expr','evaluate']