def deepcopy(val, depth=0):
    """Return a copy of a value. For immutable values, this returns the
    value itself. For mutables, it returns a deep copy.

    This presumes that the value is DB-storable. Therefore, the only
    mutable types are list and dict. (And dict keys are always strings.)

    We include a guard against loopy data structures. These raise
    TypeError.
    """
    if isinstance(val, list):
        if depth >= 8:
            raise TypeError('Database object cannot be recursive')
        newdepth = depth+1
        return [ deepcopy(subval, depth=newdepth) for subval in val ]
    if isinstance(val, dict):
        if depth >= 8:
            raise TypeError('Database object cannot be recursive')
        newdepth = depth+1
        return { key:deepcopy(subval, depth=newdepth) for (key, subval) in val.items() }
    return val