Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

String To Dict Problem

10 views
Skip to first unread message

Kamilche

unread,
Mar 26, 2006, 12:28:34 PM3/26/06
to
Hi everyone. I'm trying to convert a string that looks like this:

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

Michael Spencer

unread,
Mar 26, 2006, 1:04:41 PM3/26/06
to pytho...@python.org

Kamilche

unread,
Mar 26, 2006, 1:22:51 PM3/26/06
to
Thanks! It's interesting, and nearly what I want, but not quite there.

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?

Michael Spencer

unread,
Mar 26, 2006, 1:53:36 PM3/26/06
to pytho...@python.org
You could add a Keyword node, and use it something like this:

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

Kamilche

unread,
Mar 26, 2006, 2:05:29 PM3/26/06
to
Ah, finally, that's exactly what I need! Thanks bunches. I was
attempting to modify your first code to fit my needs, but mine was much
longer, and not yet working, a sure clue that yours is a better
solution. :-D

lalo.m...@gmail.com

unread,
Mar 29, 2006, 12:24:12 AM3/29/06
to
> def default(self, node, **kw):
> for child in node.getChildNodes():
> return self.visit(child, **kw)
>
> visitExpression = default

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'}

Clodoaldo Pinto

unread,
Apr 21, 2006, 9:40:29 PM4/21/06
to
Michael Spencer wrote:

> 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

Felipe Almeida Lessa

unread,
Apr 21, 2006, 10:37:28 PM4/21/06
to Clodoaldo Pinto, pytho...@python.org
Em Sex, 2006-04-21 às 18:40 -0700, Clodoaldo Pinto escreveu:
> Only a small problem when I try to evaluate this:
>
> safe_eval('True')

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.

Michael Spencer

unread,
Apr 21, 2006, 10:15:57 PM4/21/06
to pytho...@python.org
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)

Cheers
Michael

Clodoaldo Pinto

unread,
Apr 22, 2006, 7:35:44 AM4/22/06
to
Michael Spencer wrote:

> 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

0 new messages