# Early, and incomplete implementation of -04. # import re import urllib RESERVED = ":/?#[]@!$&'()*+,;=" OPERATOR = "+./;?|!@" EXPLODE = "*+" MODIFIER = ":^" TEMPLATE = re.compile(r"{(?P[\+\./;\?|!@])?(?P[^}]+)}", re.UNICODE) VAR = re.compile(r"^(?P[^=\+\*:\^]+)((?P[\+\*])|(?P[:\^]-?[0-9]+))?(=(?P.*))?$", 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)