2
Takes GET/POST variable dictionary, as might be returned by ``cgi``,
3
and turns them into lists and dictionaries.
5
Keys (variable names) can have subkeys, with a ``.`` and
6
can be numbered with ``-``, like ``a.b-3=something`` means that
7
the value ``a`` is a dictionary with a key ``b``, and ``b``
8
is a list, the third(-ish) element with the value ``something``.
9
Numbers are used to sort, missing numbers are ignored.
11
This doesn't deal with multiple keys, like in a query string of
12
``id=10&id=20``, which returns something like ``{'id': ['10',
13
'20']}``. That's left to someplace else to interpret. If you want to
14
represent lists in this model, you use indexes, and the lists are
17
If you want to change the character that determines when to split for
18
a dict or list, both variable_decode and variable_encode take dict_char
19
and list_char keyword args. For example, to have the GET/POST variables,
20
``a_1=something`` as a list, you would use a list_char='_'.
25
__all__ = ['variable_decode', 'variable_encode', 'NestedVariables']
27
def variable_decode(d, dict_char='.', list_char='-'):
29
Decodes the flat dictionary d into a nested structure.
34
for key, value in d.items():
35
keys = key.split(dict_char)
37
was_repetition_count = False
39
if key.endswith('--repetitions'):
40
key = key[:-len('--repetitions')]
42
known_lengths[tuple(new_keys)] = int(value)
43
was_repetition_count = True
45
elif list_char in key:
46
key, index = key.split(list_char)
48
dicts_to_sort[tuple(new_keys)] = 1
49
new_keys.append(int(index))
52
if was_repetition_count:
56
for i in range(len(new_keys)-1):
58
if not isinstance(place[new_keys[i]], dict):
59
place[new_keys[i]] = {None: place[new_keys[i]]}
60
place = place[new_keys[i]]
62
place[new_keys[i]] = {}
63
place = place[new_keys[i]]
64
if place.has_key(new_keys[-1]):
65
if isinstance(place[new_keys[-1]], dict):
66
place[new_keys[-1]][None] = value
67
elif isinstance(place[new_keys[-1]], list):
68
if isinstance(value, list):
69
place[new_keys[-1]].extend(value)
71
place[new_keys[-1]].append(value)
73
if isinstance(value, list):
74
place[new_keys[-1]] = [place[new_keys[-1]]]
75
place[new_keys[-1]].extend(value)
77
place[new_keys[-1]] = [place[new_keys[-1]], value]
79
place[new_keys[-1]] = value
81
to_sort_keys = dicts_to_sort.keys()
82
to_sort_keys.sort(lambda a, b: -cmp(len(a), len(b)))
83
for key in to_sort_keys:
90
to_sort = to_sort[sub_key]
91
if to_sort.has_key(None):
92
noneVals = [(0, x) for x in to_sort[None]]
94
noneVals.extend(to_sort.items())
97
to_sort = to_sort.items()
99
to_sort = [v for k, v in to_sort]
100
if known_lengths.has_key(key):
101
if len(to_sort) < known_lengths[key]:
102
to_sort.extend(['']*(known_lengths[key] - len(to_sort)))
103
source[last_key] = to_sort
107
def variable_encode(d, prepend='', result=None, add_repetitions=True,
108
dict_char='.', list_char='-'):
110
Encodes a nested structure into a flat dictionary.
114
if isinstance(d, dict):
115
for key, value in d.items():
121
name = "%s%s%s" % (prepend, dict_char, key)
122
variable_encode(value, name, result, add_repetitions,
123
dict_char=dict_char, list_char=list_char)
124
elif isinstance(d, list):
125
for i in range(len(d)):
126
variable_encode(d[i], "%s%s%i" % (prepend, list_char, i), result,
127
add_repetitions, dict_char=dict_char, list_char=list_char)
130
repName = '%s--repetitions' % prepend
132
repName = '__repetitions__'
133
result[repName] = str(len(d))
138
class NestedVariables(api.FancyValidator):
140
def _to_python(self, value, state):
141
return variable_decode(value)
143
def _from_python(self, value, state):
144
return variable_encode(value)
146
def empty_value(self, value):