gid = 'FPS', type = 'Label', pos = [0, 20], text = 'FPS', text2 = 'more
text without quotes', fmtline = "@VALUE @SIGNAL", signals = [('FPS',
None), ('FPS2', 'something')]
to a dict that looks like this:
{'signals': [('FPS', None), ('FPS2', 'something')], 'text': 'FPS',
'pos': [0, 20], 'text2': 'more text without quotes', 'gid': 'FPS',
'type': 'Label', 'fmtline': '@VALUE @SIGNAL'}
I've got a home-rolled routine that was 'good enough', until I added
the list of tuples in there. Now it's failing. I have a hack to
'special case' it, but you know that will come up to bite me in the
future.
Does anyone have a thought on how I can turn what are basically
function keyword arguments in string form, to a dict, without using
exec or eval?
--Kamilche
When I run my sample code through it, I get a syntax error because it's
not a valid expression. If I were to put a 'dict(' in front and a ')'
at the end, THEN it nearly works - but it gives me an
'Unsafe_Source_Error: Line 1. Unsupported source construct:
compiler.ast.CallFunc' error.
How do I let one measly function in (dict), without letting them all in?
import compiler
class SafeEval(object):
def visit(self, node,**kw):
cls = node.__class__
meth = getattr(self,'visit'+cls.__name__,self.default)
return meth(node, **kw)
def default(self, node, **kw):
for child in node.getChildNodes():
return self.visit(child, **kw)
visitExpression = default
def visitConst(self, node, **kw):
return node.value
def visitDict(self,node,**kw):
return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
def visitTuple(self,node, **kw):
return tuple(self.visit(i) for i in node.nodes)
def visitList(self,node, **kw):
return [self.visit(i) for i in node.nodes]
def visitKeyword(self,node,**kw):
return node.name, self.visit(node.expr)
def safe_dict(source):
source = "dict(%s)" % source # funcname is actually ignored
walker = SafeEval()
ast = compiler.parse(source,"eval")
kwargs = {}
args = ast.node.args
for arg in args:
if isinstance(arg, compiler.ast.Keyword):
keyword, value = walker.visit(arg)
kwargs[keyword] = value
else:
raise Exception, "only keywords"
return dict(**kwargs)
>>> source= """gid = 'FPS', type = 'Label', pos = [0, 20], text = 'FPS', text2
= 'more text without quotes', fmtline = "@VALUE @SIGNAL", signals =
[('FPS',None), ('FPS2', 'something')]"""
>>> safe_dict(source)
{'signals': [('FPS', None), ('FPS2', 'something')], 'text2': 'more text without
quotes', 'gid': 'FPS', 'fmtline': '@VALUE @SIGNAL', 'text': 'FPS', 'type':
'Label', 'pos': [0, 20]}
>>>
Michael
I'm not sure I grok this part. It leads to unexpected results:
>>> safe_dict("""gid = 'FPS', type = 'Label', pos = [0, 20], text = 'FPS', text2
= 'more text without quotes', fmtline = "@VALUE @SIGNAL", signals =
[('FPS',None), ('FPS2', 'something')], danger = 2+2""")
{'text2': 'more text without quotes', 'danger': 2, 'fmtline': '@VALUE
@SIGNAL', 'text': 'FPS', 'pos': [0, 20], 'signals': [('FPS', None),
('FPS2', 'something')], 'gid': 'FPS', 'type': 'Label'}
>>> safe_dict("""gid = 'FPS', type = 'Label', pos = [0, 20], text = 'FPS', text2
= 'more text without quotes', fmtline = "@VALUE @SIGNAL", signals =
[('FPS',None), ('FPS2', 'something')], danger = foo()""")
{'text2': 'more text without quotes', 'danger': None, 'fmtline':
'@VALUE @SIGNAL', 'text': 'FPS', 'pos': [0, 20], 'signals': [('FPS',
None), ('FPS2', 'something')], 'gid': 'FPS', 'type': 'Label'}
> http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/364469
Very nice work. It will be very useful. Thanks.
Only a small problem when I try to evaluate this:
safe_eval('True')
I get:
Traceback (most recent call last):
File "safe_eval.py", line 63, in ?
safe_eval('True')
File "safe_eval.py", line 59, in safe_eval
return walker.visit(ast)
File "safe_eval.py", line 19, in visit
return meth(node, **kw)
File "safe_eval.py", line 23, in default
return self.visit(child, **kw)
File "safe_eval.py", line 19, in visit
return meth(node, **kw)
File "safe_eval.py", line 47, in visitName
node.name, node)
__main__.Unsafe_Source_Error: Line 1. Strings must be quoted: True
This is just to let you know. I can live with that. I just replace True
for 1.
Regards, Clodoaldo Pinto
Change
def visitName(self,node, **kw):
raise Unsafe_Source_Error("Strings must be quoted",
node.name, node)
To
otherNames = {
'True': True,
'False': False,
'None': None
}
def visitName(self, node, **kw):
name = node.name
try:
return self.__class__.otherNames[name]
except KeyError:
raise Unsafe_Source_Error("Strings must be quoted",
name, node)
--
Felipe.
allowed = {"True": True, "False": False}
def visitName(self,node, **kw):
try:
return self.allowed[node.name]
except KeyError:
raise Unsafe_Source_Error("Strings must be quoted",
node.name, node)
Cheers
Michael
> Alternatively, you could edit visitName to allow 'True' and any other
> identifiers you specify e.g. (untested):
>
> allowed = {"True": True, "False": False}
> def visitName(self,node, **kw):
> try:
> return self.allowed[node.name]
> except KeyError:
> raise Unsafe_Source_Error("Strings must be quoted",
> node.name, node)
>
Thank you both Michael and Felipe. The solutions work great!
Regards, Clodoaldo