Joe Gregorio | 48d361f | 2010-08-18 13:19:21 -0400 | [diff] [blame] | 1 | # Early, and incomplete implementation of -04. |
| 2 | # |
| 3 | import re |
| 4 | import urllib |
| 5 | |
| 6 | RESERVED = ":/?#[]@!$&'()*+,;=" |
| 7 | OPERATOR = "+./;?|!@" |
| 8 | EXPLODE = "*+" |
| 9 | MODIFIER = ":^" |
| 10 | TEMPLATE = re.compile(r"{(?P<operator>[\+\./;\?|!@])?(?P<varlist>[^}]+)}", re.UNICODE) |
| 11 | VAR = re.compile(r"^(?P<varname>[^=\+\*:\^]+)((?P<explode>[\+\*])|(?P<partial>[:\^]-?[0-9]+))?(=(?P<default>.*))?$", re.UNICODE) |
| 12 | |
| 13 | def _tostring(varname, value, explode, operator, safe=""): |
| 14 | if type(value) == type([]): |
| 15 | if explode == "+": |
| 16 | return ",".join([varname + "." + urllib.quote(x, safe) for x in value]) |
| 17 | else: |
| 18 | return ",".join([urllib.quote(x, safe) for x in value]) |
| 19 | if type(value) == type({}): |
| 20 | keys = value.keys() |
| 21 | keys.sort() |
| 22 | if explode == "+": |
| 23 | return ",".join([varname + "." + urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys]) |
| 24 | else: |
| 25 | return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys]) |
| 26 | else: |
| 27 | return urllib.quote(value, safe) |
| 28 | |
| 29 | |
| 30 | def _tostring_path(varname, value, explode, operator, safe=""): |
| 31 | joiner = operator |
| 32 | if type(value) == type([]): |
| 33 | if explode == "+": |
| 34 | return joiner.join([varname + "." + urllib.quote(x, safe) for x in value]) |
| 35 | elif explode == "*": |
| 36 | return joiner.join([urllib.quote(x, safe) for x in value]) |
| 37 | else: |
| 38 | return ",".join([urllib.quote(x, safe) for x in value]) |
| 39 | elif type(value) == type({}): |
| 40 | keys = value.keys() |
| 41 | keys.sort() |
| 42 | if explode == "+": |
| 43 | return joiner.join([varname + "." + urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys]) |
| 44 | elif explode == "*": |
| 45 | return joiner.join([urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys]) |
| 46 | else: |
| 47 | return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys]) |
| 48 | else: |
| 49 | if value: |
| 50 | return urllib.quote(value, safe) |
| 51 | else: |
| 52 | return "" |
| 53 | |
| 54 | def _tostring_query(varname, value, explode, operator, safe=""): |
| 55 | joiner = operator |
| 56 | varprefix = "" |
| 57 | if operator == "?": |
| 58 | joiner = "&" |
| 59 | varprefix = varname + "=" |
| 60 | if type(value) == type([]): |
| 61 | if 0 == len(value): |
| 62 | return "" |
| 63 | if explode == "+": |
| 64 | return joiner.join([varname + "=" + urllib.quote(x, safe) for x in value]) |
| 65 | elif explode == "*": |
| 66 | return joiner.join([urllib.quote(x, safe) for x in value]) |
| 67 | else: |
| 68 | return varprefix + ",".join([urllib.quote(x, safe) for x in value]) |
| 69 | elif type(value) == type({}): |
| 70 | if 0 == len(value): |
| 71 | return "" |
| 72 | keys = value.keys() |
| 73 | keys.sort() |
| 74 | if explode == "+": |
| 75 | return joiner.join([varname + "." + urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys]) |
| 76 | elif explode == "*": |
| 77 | return joiner.join([urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys]) |
| 78 | else: |
| 79 | return varprefix + ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys]) |
| 80 | else: |
| 81 | if value: |
| 82 | return varname + "=" + urllib.quote(value, safe) |
| 83 | else: |
| 84 | return varname |
| 85 | |
| 86 | TOSTRING = { |
| 87 | "" : _tostring, |
| 88 | "+": _tostring, |
| 89 | ";": _tostring_query, |
| 90 | "?": _tostring_query, |
| 91 | "/": _tostring_path, |
| 92 | ".": _tostring_path, |
| 93 | } |
| 94 | |
| 95 | |
| 96 | def expand(template, vars): |
| 97 | def _sub(match): |
| 98 | groupdict = match.groupdict() |
| 99 | operator = groupdict.get('operator') |
| 100 | if operator is None: |
| 101 | operator = '' |
| 102 | varlist = groupdict.get('varlist') |
| 103 | |
| 104 | safe = "" |
| 105 | if operator == '+': |
| 106 | safe = RESERVED |
| 107 | varspecs = varlist.split(",") |
| 108 | varnames = [] |
| 109 | defaults = {} |
| 110 | for varspec in varspecs: |
| 111 | m = VAR.search(varspec) |
| 112 | groupdict = m.groupdict() |
| 113 | varname = groupdict.get('varname') |
| 114 | explode = groupdict.get('explode') |
| 115 | partial = groupdict.get('partial') |
| 116 | default = groupdict.get('default') |
| 117 | if default: |
| 118 | defaults[varname] = default |
| 119 | varnames.append((varname, explode, partial)) |
| 120 | |
| 121 | retval = [] |
| 122 | joiner = operator |
| 123 | prefix = operator |
| 124 | if operator == "+": |
| 125 | prefix = "" |
| 126 | joiner = "," |
| 127 | if operator == "?": |
| 128 | joiner = "&" |
| 129 | if operator == "": |
| 130 | joiner = "," |
| 131 | for varname, explode, partial in varnames: |
| 132 | if varname in vars: |
| 133 | value = vars[varname] |
| 134 | #if not value and (type(value) == type({}) or type(value) == type([])) and varname in defaults: |
| 135 | if not value and value != "" and varname in defaults: |
| 136 | value = defaults[varname] |
| 137 | elif varname in defaults: |
| 138 | value = defaults[varname] |
| 139 | else: |
| 140 | continue |
| 141 | retval.append(TOSTRING[operator](varname, value, explode, operator, safe=safe)) |
| 142 | if "".join(retval): |
| 143 | return prefix + joiner.join(retval) |
| 144 | else: |
| 145 | return "" |
| 146 | |
| 147 | return TEMPLATE.sub(_sub, template) |