Code: Rolling a Container Into a String

Kamilche klachemin at home.com
Fri Jun 25 03:42:35 CEST 2004


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()



More information about the Python-list mailing list