mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-04 21:01:36 +00:00
update uritemplate
This commit is contained in:
@@ -1,147 +1,265 @@
|
|||||||
# Early, and incomplete implementation of -04.
|
#!/usr/bin/env python
|
||||||
#
|
|
||||||
|
"""
|
||||||
|
URI Template (RFC6570) Processor
|
||||||
|
"""
|
||||||
|
|
||||||
|
__copyright__ = """\
|
||||||
|
Copyright 2011-2013 Joe Gregorio
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import urllib
|
try:
|
||||||
|
from urllib.parse import quote
|
||||||
|
except ImportError:
|
||||||
|
from urllib import quote
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = "0.6"
|
||||||
|
|
||||||
RESERVED = ":/?#[]@!$&'()*+,;="
|
RESERVED = ":/?#[]@!$&'()*+,;="
|
||||||
OPERATOR = "+./;?|!@"
|
OPERATOR = "+#./;?&|!@"
|
||||||
EXPLODE = "*+"
|
|
||||||
MODIFIER = ":^"
|
MODIFIER = ":^"
|
||||||
TEMPLATE = re.compile(r"{(?P<operator>[\+\./;\?|!@])?(?P<varlist>[^}]+)}", re.UNICODE)
|
TEMPLATE = re.compile("{([^\}]+)}")
|
||||||
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=""):
|
def variables(template):
|
||||||
joiner = operator
|
'''Returns the set of keywords in a uri template'''
|
||||||
if type(value) == type([]):
|
vars = set()
|
||||||
if explode == "+":
|
for varlist in TEMPLATE.findall(template):
|
||||||
return joiner.join([varname + "." + urllib.quote(x, safe) for x in value])
|
if varlist[0] in OPERATOR:
|
||||||
elif explode == "*":
|
varlist = varlist[1:]
|
||||||
return joiner.join([urllib.quote(x, safe) for x in value])
|
varspecs = varlist.split(',')
|
||||||
else:
|
for var in varspecs:
|
||||||
return ",".join([urllib.quote(x, safe) for x in value])
|
# handle prefix values
|
||||||
elif type(value) == type({}):
|
var = var.split(':')[0]
|
||||||
keys = value.keys()
|
# handle composite values
|
||||||
keys.sort()
|
if var.endswith('*'):
|
||||||
if explode == "+":
|
var = var[:-1]
|
||||||
return joiner.join([varname + "." + urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
|
vars.add(var)
|
||||||
elif explode == "*":
|
return vars
|
||||||
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
|
def _quote(value, safe, prefix=None):
|
||||||
varprefix = ""
|
if prefix is not None:
|
||||||
if operator == "?":
|
return quote(str(value)[:prefix], safe)
|
||||||
joiner = "&"
|
return quote(str(value), safe)
|
||||||
varprefix = varname + "="
|
|
||||||
if type(value) == type([]):
|
|
||||||
if 0 == len(value):
|
def _tostring(varname, value, explode, prefix, operator, safe=""):
|
||||||
return ""
|
if isinstance(value, list):
|
||||||
if explode == "+":
|
return ",".join([_quote(x, safe) for x in value])
|
||||||
return joiner.join([varname + "=" + urllib.quote(x, safe) for x in value])
|
if isinstance(value, dict):
|
||||||
elif explode == "*":
|
keys = sorted(value.keys())
|
||||||
return joiner.join([urllib.quote(x, safe) for x in value])
|
if explode:
|
||||||
|
return ",".join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
else:
|
||||||
|
return ",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
elif value is None:
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
return varprefix + ",".join([urllib.quote(x, safe) for x in value])
|
return _quote(value, safe, prefix)
|
||||||
elif type(value) == type({}):
|
|
||||||
if 0 == len(value):
|
|
||||||
return ""
|
def _tostring_path(varname, value, explode, prefix, operator, safe=""):
|
||||||
keys = value.keys()
|
joiner = operator
|
||||||
keys.sort()
|
if isinstance(value, list):
|
||||||
if explode == "+":
|
if explode:
|
||||||
return joiner.join([varname + "." + urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
out = [_quote(x, safe) for x in value if value is not None]
|
||||||
elif explode == "*":
|
else:
|
||||||
return joiner.join([urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
joiner = ","
|
||||||
|
out = [_quote(x, safe) for x in value if value is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
out = [_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) for key in keys \
|
||||||
|
if value[key] is not None]
|
||||||
|
else:
|
||||||
|
joiner = ","
|
||||||
|
out = [_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys if value[key] is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
elif value is None:
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
return varprefix + ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
return _quote(value, safe, prefix)
|
||||||
else:
|
|
||||||
if value:
|
|
||||||
return varname + "=" + urllib.quote(value, safe)
|
def _tostring_semi(varname, value, explode, prefix, operator, safe=""):
|
||||||
|
joiner = operator
|
||||||
|
if operator == "?":
|
||||||
|
joiner = "&"
|
||||||
|
if isinstance(value, list):
|
||||||
|
if explode:
|
||||||
|
out = [varname + "=" + _quote(x, safe) \
|
||||||
|
for x in value if x is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return varname + "=" + ",".join([_quote(x, safe) \
|
||||||
|
for x in value])
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
return joiner.join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys if key is not None])
|
||||||
|
else:
|
||||||
|
return varname + "=" + ",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys \
|
||||||
|
if key is not None])
|
||||||
else:
|
else:
|
||||||
return varname
|
if value is None:
|
||||||
|
return
|
||||||
|
elif value:
|
||||||
|
return (varname + "=" + _quote(value, safe, prefix))
|
||||||
|
else:
|
||||||
|
return varname
|
||||||
|
|
||||||
|
|
||||||
|
def _tostring_query(varname, value, explode, prefix, operator, safe=""):
|
||||||
|
joiner = operator
|
||||||
|
if operator in ["?", "&"]:
|
||||||
|
joiner = "&"
|
||||||
|
if isinstance(value, list):
|
||||||
|
if 0 == len(value):
|
||||||
|
return None
|
||||||
|
if explode:
|
||||||
|
return joiner.join([varname + "=" + _quote(x, safe) \
|
||||||
|
for x in value])
|
||||||
|
else:
|
||||||
|
return (varname + "=" + ",".join([_quote(x, safe) \
|
||||||
|
for x in value]))
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
if 0 == len(value):
|
||||||
|
return None
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
return joiner.join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys])
|
||||||
|
else:
|
||||||
|
return varname + "=" + \
|
||||||
|
",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
else:
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
elif value:
|
||||||
|
return (varname + "=" + _quote(value, safe, prefix))
|
||||||
|
else:
|
||||||
|
return (varname + "=")
|
||||||
|
|
||||||
|
|
||||||
TOSTRING = {
|
TOSTRING = {
|
||||||
"" : _tostring,
|
"" : _tostring,
|
||||||
"+": _tostring,
|
"+": _tostring,
|
||||||
";": _tostring_query,
|
"#": _tostring,
|
||||||
|
";": _tostring_semi,
|
||||||
"?": _tostring_query,
|
"?": _tostring_query,
|
||||||
|
"&": _tostring_query,
|
||||||
"/": _tostring_path,
|
"/": _tostring_path,
|
||||||
".": _tostring_path,
|
".": _tostring_path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def expand(template, vars):
|
def expand(template, variables):
|
||||||
def _sub(match):
|
"""
|
||||||
groupdict = match.groupdict()
|
Expand template as a URI Template using variables.
|
||||||
operator = groupdict.get('operator')
|
"""
|
||||||
if operator is None:
|
def _sub(match):
|
||||||
operator = ''
|
expression = match.group(1)
|
||||||
varlist = groupdict.get('varlist')
|
operator = ""
|
||||||
|
if expression[0] in OPERATOR:
|
||||||
|
operator = expression[0]
|
||||||
|
varlist = expression[1:]
|
||||||
|
else:
|
||||||
|
varlist = expression
|
||||||
|
|
||||||
safe = "@"
|
safe = ""
|
||||||
if operator == '+':
|
if operator in ["+", "#"]:
|
||||||
safe = RESERVED
|
safe = RESERVED
|
||||||
varspecs = varlist.split(",")
|
varspecs = varlist.split(",")
|
||||||
varnames = []
|
varnames = []
|
||||||
defaults = {}
|
defaults = {}
|
||||||
for varspec in varspecs:
|
for varspec in varspecs:
|
||||||
m = VAR.search(varspec)
|
default = None
|
||||||
groupdict = m.groupdict()
|
explode = False
|
||||||
varname = groupdict.get('varname')
|
prefix = None
|
||||||
explode = groupdict.get('explode')
|
if "=" in varspec:
|
||||||
partial = groupdict.get('partial')
|
varname, default = tuple(varspec.split("=", 1))
|
||||||
default = groupdict.get('default')
|
else:
|
||||||
if default:
|
varname = varspec
|
||||||
defaults[varname] = default
|
if varname[-1] == "*":
|
||||||
varnames.append((varname, explode, partial))
|
explode = True
|
||||||
|
varname = varname[:-1]
|
||||||
|
elif ":" in varname:
|
||||||
|
try:
|
||||||
|
prefix = int(varname[varname.index(":")+1:])
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("non-integer prefix '{0}'".format(
|
||||||
|
varname[varname.index(":")+1:]))
|
||||||
|
varname = varname[:varname.index(":")]
|
||||||
|
if default:
|
||||||
|
defaults[varname] = default
|
||||||
|
varnames.append((varname, explode, prefix))
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
joiner = operator
|
joiner = operator
|
||||||
prefix = operator
|
start = operator
|
||||||
if operator == "+":
|
if operator == "+":
|
||||||
prefix = ""
|
start = ""
|
||||||
joiner = ","
|
joiner = ","
|
||||||
if operator == "?":
|
if operator == "#":
|
||||||
joiner = "&"
|
joiner = ","
|
||||||
if operator == "":
|
if operator == "?":
|
||||||
joiner = ","
|
joiner = "&"
|
||||||
for varname, explode, partial in varnames:
|
if operator == "&":
|
||||||
if varname in vars:
|
start = "&"
|
||||||
value = vars[varname]
|
if operator == "":
|
||||||
#if not value and (type(value) == type({}) or type(value) == type([])) and varname in defaults:
|
joiner = ","
|
||||||
if not value and value != "" and varname in defaults:
|
for varname, explode, prefix in varnames:
|
||||||
value = defaults[varname]
|
if varname in variables:
|
||||||
elif varname in defaults:
|
value = variables[varname]
|
||||||
value = defaults[varname]
|
if not value and value != "" and varname in defaults:
|
||||||
else:
|
value = defaults[varname]
|
||||||
continue
|
elif varname in defaults:
|
||||||
retval.append(TOSTRING[operator](varname, value, explode, operator, safe=safe))
|
value = defaults[varname]
|
||||||
if "".join(retval):
|
else:
|
||||||
return prefix + joiner.join(retval)
|
continue
|
||||||
else:
|
expanded = TOSTRING[operator](
|
||||||
return ""
|
varname, value, explode, prefix, operator, safe=safe)
|
||||||
|
if expanded is not None:
|
||||||
|
retval.append(expanded)
|
||||||
|
if len(retval) > 0:
|
||||||
|
return start + joiner.join(retval)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
return TEMPLATE.sub(_sub, template)
|
return TEMPLATE.sub(_sub, template)
|
||||||
|
|||||||
Reference in New Issue
Block a user