import re from urllib.parse import quote, unquote from sanic_routing.exceptions import InvalidUsage from .patterns import REGEX_PARAM_NAME, REGEX_PARAM_NAME_EXT class Immutable(dict): def __setitem__(self, *args): raise TypeError("Cannot change immutable dict") def __delitem__(self, *args): raise TypeError("Cannot change immutable dict") def parse_parameter_basket(route, basket, raw_path=None): params = {} if basket: for idx, value in basket.items(): for p in route.params[idx]: if not raw_path or p.raw_path == raw_path: if not p.regex: raw_path = p.raw_path params[p.name] = p.cast(value) break elif p.pattern.search(value): raw_path = p.raw_path if "(" in p.pattern: groups = p.pattern.match(value) value = groups.group(1) params[p.name] = p.cast(value) break if raw_path: raise ValueError("Invalid parameter") if raw_path and not params[p.name]: raise ValueError("Invalid parameter") if route.unquote: for p in route.params[idx]: if isinstance(params[p.name], str): params[p.name] = unquote(params[p.name]) if raw_path is None: raise ValueError("Invalid parameter") return params, raw_path def path_to_parts(path, delimiter="/"): r""" OK > /foo//bar/ OK > /foo/ OK > /foo/txt)>/ OK > /foo// OK > /foo//txt)d> NOT OK > /foo/txt)d>/ """ path = unquote(path.lstrip(delimiter)) delimiter = re.escape(delimiter) return tuple( part if part.startswith("<") else quote(part) for part in re.split(rf"{delimiter}(?=[^>]*(?:<(?") except AttributeError: try: match = REGEX_PARAM_NAME_EXT.match(part) filename_type = "" extension_type = "" if match.group(2): filename_type = f"={match.group(2)}" if match.group(3): extension_type = f"={match.group(3)}" segment = ( f"<{match.group(1)}{filename_type}:" f"ext{extension_type}>" ) path.append(segment) except AttributeError: raise InvalidUsage(f"Invalid declaration: {part}") else: path.append(part) return delimiter.join(path)