I want to convert a dict into string form, then back again. After
discovering that eval is insecure, I wrote some code to roll a Python
object, dict, tuple, or list into a string. I've posted it below. Does
anyone know an easier way to accomplish this? Essentially, I want to
avoid doing an 'eval' on a string to get it back into dict form... but
still allow nested structures. (My current code doesn't handle nested
structures.)
I conked out before writing the 'unroll' code. I'm going to go watch
some boob tube with my husband, instead, and leave that code for
another day. If you know of a better way, or feel like writing the
'unroll' code and posting it, by all means, do so! :-D I'll check
Google newsgroups before I tackle the job, to see if some kind soul
took pity on me.
--Kamilche
import types
SimpleTypes = [types.BooleanType, types.FloatType, types.IntType, \
types.LongType, types.NoneType, types.StringType]
_dictdelim1 = "{"
_dictdelim2 = "}"
_listdelim1 = "["
_listdelim2 = "]"
_tupledelim1 = "("
_tupledelim2 = ")"
def roll(item):
"Return a string representation of an object, dict, tuple, or
list."
return _roll2(item, [], {})
def unroll(s):
"Unrolls a string back into a dict, tuple, or list."
if type(s) != types.StringType:
raise Exception("You may only pass strings to this function!")
err = "Error occurred when parsing " + s + "!"
state = 0
container = None
for c in s:
if c == _dictdelim1:
lookfor = _dictdelim2
elif c == _listdelim1:
lookfor = _listdelim2
elif c == _tupledelim1:
lookfor = _tupledelim2
else:
raise Exception(err)
state = 1
def _quoted(s):
' Return a stringized value'
if type(s) != types.StringType:
return str(s)
else:
l = []
s = s.replace("'", "\'")
l.append("'")
l.append(s)
l.append("'")
return ''.join(l)
def _roll2(d, lst, r):
' Function that does the work.'
# Start of _roll2
t = type(d)
if t == types.DictType:
theid = id(d)
if theid in r:
raise Exception("Recursion detected! Stopping now.")
r[theid] = theid
cnt = 0
lst.append(_dictdelim1)
for key in d.keys():
if key[0] != '_':
lst.append(_quoted(key))
lst.append(': ')
t = type(d[key])
if t in SimpleTypes:
lst.append(_quoted(d[key]))
else:
_roll2(d[key], lst, r)
lst.append(", ")
cnt += 1
if cnt > 0:
del lst[-1]
lst.append(_dictdelim2)
elif t in (types.ListType, types.TupleType):
theid = id(d)
if theid in r:
raise Exception("Recursion detected! Stopping now.")
r[theid] = theid
cnt = 0
if t == types.ListType:
lst.append(_listdelim1)
else:
lst.append(_tupledelim1)
for item in d:
if type(item) in SimpleTypes:
lst.append(_quoted(item))
else:
_roll2(item, lst, r)
lst.append(", ")
cnt += 1
if cnt > 0:
del lst[-1]
if t == types.ListType:
lst.append(_listdelim2)
else:
lst.append(_tupledelim2)
elif hasattr(d, '__dict__'):
_roll2(d.__dict__, lst, r)
else:
raise Exception("Unhandled type " + str(t) + \
"! You may only pass dicts, tuples, lists, and
" + \
"objects with a __dict__ to this function!")
return ''.join(lst)
class simple:
pass
def main():
l = ['List1', 'List2', 'List3']
d = {'Dict1': 'd1', 'Dict2': 'd2', 'list': l}
t = ('Tuple1', d, 'Tuple2')
print "It handles dicts, lists, and tuples."
print roll(t), "\n"
o = simple()
o.name = 'the name'
o.password = 'the password'
o.list = ['ol1', 'ol2']
o.dict = {'od1': None, 'od2': 2}
o.tuple = ('tuple1', 'tuple2')
o.float = 1.5
o.long = 12345678901234567890
o.bool = True
o.int = 1
print "It handles objects."
print roll(o), "\n"
print "It won't roll attributes whose name starts with '_'"
o._recursion = o.tuple
print roll(o), "\n"
print "It will raise an exception if it detects recursion."
print "This next one will cause recursion."
o.recursion = o.tuple
print roll(o), "\n"
print "You can't roll simple types."
print roll('a'), "\n"
if __name__ == "__main__":
main()