mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-19 13:41:36 +00:00
266 lines
8.0 KiB
Python
266 lines
8.0 KiB
Python
#!/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
|
|
try:
|
|
from urllib.parse import quote
|
|
except ImportError:
|
|
from urllib import quote
|
|
|
|
|
|
|
|
__version__ = "0.6"
|
|
|
|
RESERVED = ":/?#[]@!$&'()*+,;="
|
|
OPERATOR = "+#./;?&|!@"
|
|
MODIFIER = ":^"
|
|
TEMPLATE = re.compile("{([^\}]+)}")
|
|
|
|
|
|
def variables(template):
|
|
'''Returns the set of keywords in a uri template'''
|
|
vars = set()
|
|
for varlist in TEMPLATE.findall(template):
|
|
if varlist[0] in OPERATOR:
|
|
varlist = varlist[1:]
|
|
varspecs = varlist.split(',')
|
|
for var in varspecs:
|
|
# handle prefix values
|
|
var = var.split(':')[0]
|
|
# handle composite values
|
|
if var.endswith('*'):
|
|
var = var[:-1]
|
|
vars.add(var)
|
|
return vars
|
|
|
|
|
|
def _quote(value, safe, prefix=None):
|
|
if prefix is not None:
|
|
return quote(str(value)[:prefix], safe)
|
|
return quote(str(value), safe)
|
|
|
|
|
|
def _tostring(varname, value, explode, prefix, operator, safe=""):
|
|
if isinstance(value, list):
|
|
return ",".join([_quote(x, safe) for x in value])
|
|
if isinstance(value, dict):
|
|
keys = sorted(value.keys())
|
|
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:
|
|
return _quote(value, safe, prefix)
|
|
|
|
|
|
def _tostring_path(varname, value, explode, prefix, operator, safe=""):
|
|
joiner = operator
|
|
if isinstance(value, list):
|
|
if explode:
|
|
out = [_quote(x, safe) for x in value if value is not None]
|
|
else:
|
|
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:
|
|
return _quote(value, safe, prefix)
|
|
|
|
|
|
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:
|
|
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_semi,
|
|
"?": _tostring_query,
|
|
"&": _tostring_query,
|
|
"/": _tostring_path,
|
|
".": _tostring_path,
|
|
}
|
|
|
|
|
|
def expand(template, variables):
|
|
"""
|
|
Expand template as a URI Template using variables.
|
|
"""
|
|
def _sub(match):
|
|
expression = match.group(1)
|
|
operator = ""
|
|
if expression[0] in OPERATOR:
|
|
operator = expression[0]
|
|
varlist = expression[1:]
|
|
else:
|
|
varlist = expression
|
|
|
|
safe = ""
|
|
if operator in ["+", "#"]:
|
|
safe = RESERVED
|
|
varspecs = varlist.split(",")
|
|
varnames = []
|
|
defaults = {}
|
|
for varspec in varspecs:
|
|
default = None
|
|
explode = False
|
|
prefix = None
|
|
if "=" in varspec:
|
|
varname, default = tuple(varspec.split("=", 1))
|
|
else:
|
|
varname = varspec
|
|
if varname[-1] == "*":
|
|
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 = []
|
|
joiner = operator
|
|
start = operator
|
|
if operator == "+":
|
|
start = ""
|
|
joiner = ","
|
|
if operator == "#":
|
|
joiner = ","
|
|
if operator == "?":
|
|
joiner = "&"
|
|
if operator == "&":
|
|
start = "&"
|
|
if operator == "":
|
|
joiner = ","
|
|
for varname, explode, prefix in varnames:
|
|
if varname in variables:
|
|
value = variables[varname]
|
|
if not value and value != "" and varname in defaults:
|
|
value = defaults[varname]
|
|
elif varname in defaults:
|
|
value = defaults[varname]
|
|
else:
|
|
continue
|
|
expanded = TOSTRING[operator](
|
|
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)
|