148 lines
4.7 KiB
Python
148 lines
4.7 KiB
Python
# Early, and incomplete implementation of -04.
|
|
#
|
|
import re
|
|
import urllib
|
|
|
|
RESERVED = ":/?#[]@!$&'()*+,;="
|
|
OPERATOR = "+./;?|!@"
|
|
EXPLODE = "*+"
|
|
MODIFIER = ":^"
|
|
TEMPLATE = re.compile(r"{(?P<operator>[\+\./;\?|!@])?(?P<varlist>[^}]+)}", re.UNICODE)
|
|
VAR = re.compile(r"^(?P<varname>[^=\+\*:\^]+)((?P<explode>[\+\*])|(?P<partial>[:\^]-?[0-9]+))?(=(?P<default>.*))?$", re.UNICODE)
|
|
|
|
def _tostring(varname, value, explode, operator, safe=""):
|
|
if type(value) == type([]):
|
|
if explode == "+":
|
|
return ",".join([varname + "." + urllib.quote(x, safe) for x in value])
|
|
else:
|
|
return ",".join([urllib.quote(x, safe) for x in value])
|
|
if type(value) == type({}):
|
|
keys = value.keys()
|
|
keys.sort()
|
|
if explode == "+":
|
|
return ",".join([varname + "." + urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
return urllib.quote(value, safe)
|
|
|
|
|
|
def _tostring_path(varname, value, explode, operator, safe=""):
|
|
joiner = operator
|
|
if type(value) == type([]):
|
|
if explode == "+":
|
|
return joiner.join([varname + "." + urllib.quote(x, safe) for x in value])
|
|
elif explode == "*":
|
|
return joiner.join([urllib.quote(x, safe) for x in value])
|
|
else:
|
|
return ",".join([urllib.quote(x, safe) for x in value])
|
|
elif type(value) == type({}):
|
|
keys = value.keys()
|
|
keys.sort()
|
|
if explode == "+":
|
|
return joiner.join([varname + "." + urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
|
|
elif explode == "*":
|
|
return joiner.join([urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
if value:
|
|
return urllib.quote(value, safe)
|
|
else:
|
|
return ""
|
|
|
|
def _tostring_query(varname, value, explode, operator, safe=""):
|
|
joiner = operator
|
|
varprefix = ""
|
|
if operator == "?":
|
|
joiner = "&"
|
|
varprefix = varname + "="
|
|
if type(value) == type([]):
|
|
if 0 == len(value):
|
|
return ""
|
|
if explode == "+":
|
|
return joiner.join([varname + "=" + urllib.quote(x, safe) for x in value])
|
|
elif explode == "*":
|
|
return joiner.join([urllib.quote(x, safe) for x in value])
|
|
else:
|
|
return varprefix + ",".join([urllib.quote(x, safe) for x in value])
|
|
elif type(value) == type({}):
|
|
if 0 == len(value):
|
|
return ""
|
|
keys = value.keys()
|
|
keys.sort()
|
|
if explode == "+":
|
|
return joiner.join([varname + "." + urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
|
elif explode == "*":
|
|
return joiner.join([urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
return varprefix + ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
else:
|
|
if value:
|
|
return varname + "=" + urllib.quote(value, safe)
|
|
else:
|
|
return varname
|
|
|
|
TOSTRING = {
|
|
"" : _tostring,
|
|
"+": _tostring,
|
|
";": _tostring_query,
|
|
"?": _tostring_query,
|
|
"/": _tostring_path,
|
|
".": _tostring_path,
|
|
}
|
|
|
|
|
|
def expand(template, vars):
|
|
def _sub(match):
|
|
groupdict = match.groupdict()
|
|
operator = groupdict.get('operator')
|
|
if operator is None:
|
|
operator = ''
|
|
varlist = groupdict.get('varlist')
|
|
|
|
safe = "@"
|
|
if operator == '+':
|
|
safe = RESERVED
|
|
varspecs = varlist.split(",")
|
|
varnames = []
|
|
defaults = {}
|
|
for varspec in varspecs:
|
|
m = VAR.search(varspec)
|
|
groupdict = m.groupdict()
|
|
varname = groupdict.get('varname')
|
|
explode = groupdict.get('explode')
|
|
partial = groupdict.get('partial')
|
|
default = groupdict.get('default')
|
|
if default:
|
|
defaults[varname] = default
|
|
varnames.append((varname, explode, partial))
|
|
|
|
retval = []
|
|
joiner = operator
|
|
prefix = operator
|
|
if operator == "+":
|
|
prefix = ""
|
|
joiner = ","
|
|
if operator == "?":
|
|
joiner = "&"
|
|
if operator == "":
|
|
joiner = ","
|
|
for varname, explode, partial in varnames:
|
|
if varname in vars:
|
|
value = vars[varname]
|
|
#if not value and (type(value) == type({}) or type(value) == type([])) and varname in defaults:
|
|
if not value and value != "" and varname in defaults:
|
|
value = defaults[varname]
|
|
elif varname in defaults:
|
|
value = defaults[varname]
|
|
else:
|
|
continue
|
|
retval.append(TOSTRING[operator](varname, value, explode, operator, safe=safe))
|
|
if "".join(retval):
|
|
return prefix + joiner.join(retval)
|
|
else:
|
|
return ""
|
|
|
|
return TEMPLATE.sub(_sub, template)
|