mirror of
https://github.com/djohnlewis/stackdump
synced 2025-12-07 00:13:33 +00:00
Initial commit. Still building up the env and some parsing code.
This commit is contained in:
152
python/packages/formencode/variabledecode.py
Normal file
152
python/packages/formencode/variabledecode.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
Takes GET/POST variable dictionary, as might be returned by ``cgi``,
|
||||
and turns them into lists and dictionaries.
|
||||
|
||||
Keys (variable names) can have subkeys, with a ``.`` and
|
||||
can be numbered with ``-``, like ``a.b-3=something`` means that
|
||||
the value ``a`` is a dictionary with a key ``b``, and ``b``
|
||||
is a list, the third(-ish) element with the value ``something``.
|
||||
Numbers are used to sort, missing numbers are ignored.
|
||||
|
||||
This doesn't deal with multiple keys, like in a query string of
|
||||
``id=10&id=20``, which returns something like ``{'id': ['10',
|
||||
'20']}``. That's left to someplace else to interpret. If you want to
|
||||
represent lists in this model, you use indexes, and the lists are
|
||||
explicitly ordered.
|
||||
|
||||
If you want to change the character that determines when to split for
|
||||
a dict or list, both variable_decode and variable_encode take dict_char
|
||||
and list_char keyword args. For example, to have the GET/POST variables,
|
||||
``a_1=something`` as a list, you would use a list_char='_'.
|
||||
"""
|
||||
|
||||
import api
|
||||
|
||||
__all__ = ['variable_decode', 'variable_encode', 'NestedVariables']
|
||||
|
||||
|
||||
def variable_decode(d, dict_char='.', list_char='-'):
|
||||
"""
|
||||
Decode the flat dictionary d into a nested structure.
|
||||
"""
|
||||
result = {}
|
||||
dicts_to_sort = {}
|
||||
known_lengths = {}
|
||||
for key, value in d.items():
|
||||
keys = key.split(dict_char)
|
||||
new_keys = []
|
||||
was_repetition_count = False
|
||||
for key in keys:
|
||||
if key.endswith('--repetitions'):
|
||||
key = key[:-len('--repetitions')]
|
||||
new_keys.append(key)
|
||||
known_lengths[tuple(new_keys)] = int(value)
|
||||
was_repetition_count = True
|
||||
break
|
||||
elif list_char in key:
|
||||
key, index = key.split(list_char)
|
||||
new_keys.append(key)
|
||||
dicts_to_sort[tuple(new_keys)] = 1
|
||||
new_keys.append(int(index))
|
||||
else:
|
||||
new_keys.append(key)
|
||||
if was_repetition_count:
|
||||
continue
|
||||
|
||||
place = result
|
||||
for i in range(len(new_keys)-1):
|
||||
try:
|
||||
if not isinstance(place[new_keys[i]], dict):
|
||||
place[new_keys[i]] = {None: place[new_keys[i]]}
|
||||
place = place[new_keys[i]]
|
||||
except KeyError:
|
||||
place[new_keys[i]] = {}
|
||||
place = place[new_keys[i]]
|
||||
if new_keys[-1] in place:
|
||||
if isinstance(place[new_keys[-1]], dict):
|
||||
place[new_keys[-1]][None] = value
|
||||
elif isinstance(place[new_keys[-1]], list):
|
||||
if isinstance(value, list):
|
||||
place[new_keys[-1]].extend(value)
|
||||
else:
|
||||
place[new_keys[-1]].append(value)
|
||||
else:
|
||||
if isinstance(value, list):
|
||||
place[new_keys[-1]] = [place[new_keys[-1]]]
|
||||
place[new_keys[-1]].extend(value)
|
||||
else:
|
||||
place[new_keys[-1]] = [place[new_keys[-1]], value]
|
||||
else:
|
||||
place[new_keys[-1]] = value
|
||||
|
||||
try:
|
||||
to_sort_keys = sorted(dicts_to_sort, key=len, reverse=True)
|
||||
except NameError: # Python < 2.4
|
||||
to_sort_keys = dicts_to_sort.keys()
|
||||
to_sort_keys.sort(lambda a, b: -cmp(len(a), len(b)))
|
||||
for key in to_sort_keys:
|
||||
to_sort = result
|
||||
source = None
|
||||
last_key = None
|
||||
for sub_key in key:
|
||||
source = to_sort
|
||||
last_key = sub_key
|
||||
to_sort = to_sort[sub_key]
|
||||
if None in to_sort:
|
||||
noneVals = [(0, x) for x in to_sort.pop(None)]
|
||||
noneVals.extend(to_sort.items())
|
||||
to_sort = noneVals
|
||||
else:
|
||||
to_sort = to_sort.items()
|
||||
to_sort.sort()
|
||||
to_sort = [v for k, v in to_sort]
|
||||
if key in known_lengths:
|
||||
if len(to_sort) < known_lengths[key]:
|
||||
to_sort.extend(['']*(known_lengths[key] - len(to_sort)))
|
||||
source[last_key] = to_sort
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def variable_encode(d, prepend='', result=None, add_repetitions=True,
|
||||
dict_char='.', list_char='-'):
|
||||
"""
|
||||
Encode a nested structure into a flat dictionary.
|
||||
"""
|
||||
if result is None:
|
||||
result = {}
|
||||
if isinstance(d, dict):
|
||||
for key, value in d.items():
|
||||
if key is None:
|
||||
name = prepend
|
||||
elif not prepend:
|
||||
name = key
|
||||
else:
|
||||
name = "%s%s%s" % (prepend, dict_char, key)
|
||||
variable_encode(value, name, result, add_repetitions,
|
||||
dict_char=dict_char, list_char=list_char)
|
||||
elif isinstance(d, list):
|
||||
for i in range(len(d)):
|
||||
variable_encode(d[i], "%s%s%i" % (prepend, list_char, i), result,
|
||||
add_repetitions, dict_char=dict_char, list_char=list_char)
|
||||
if add_repetitions:
|
||||
if prepend:
|
||||
repName = '%s--repetitions' % prepend
|
||||
else:
|
||||
repName = '__repetitions__'
|
||||
result[repName] = str(len(d))
|
||||
else:
|
||||
result[prepend] = d
|
||||
return result
|
||||
|
||||
|
||||
class NestedVariables(api.FancyValidator):
|
||||
|
||||
def _to_python(self, value, state):
|
||||
return variable_decode(value)
|
||||
|
||||
def _from_python(self, value, state):
|
||||
return variable_encode(value)
|
||||
|
||||
def empty_value(self, value):
|
||||
return {}
|
||||
Reference in New Issue
Block a user