def url_for(self, view_name: str, **kwargs): """Build a URL based on a view name and the values provided. In order to build a URL, all request parameters must be supplied as keyword arguments, and each parameter must pass the test for the specified parameter type. If these conditions are not met, a `URLBuildError` will be thrown. Keyword arguments that are not request parameters will be included in the output URL's query string. There are several _special_ keyword arguments that will alter how the URL will be returned: 1. **_anchor**: ``str`` - Adds an ``#anchor`` to the end 2. **_scheme**: ``str`` - Should be either ``"http"`` or ``"https"``, default is ``"http"`` 3. **_external**: ``bool`` - Whether to return the path or a full URL with scheme and host 4. **_host**: ``str`` - Used when one or more hosts are defined for a route to tell Sanic which to use (only applies with ``_external=True``) 5. **_server**: ``str`` - If not using ``_host``, this will be used for defining the hostname of the URL (only applies with ``_external=True``), defaults to ``app.config.SERVER_NAME`` If you want the PORT to appear in your URL, you should set it in: .. code-block:: app.config.SERVER_NAME = "myserver:7777" `See user guide <https://sanicframework.org/guide/basics/routing.html#generating-a-url>`__ :param view_name: string referencing the view name :param kwargs: keys and values that are used to build request parameters and query string arguments. :return: the built URL Raises: URLBuildError """ # find the route by the supplied view name kw: Dict[str, str] = {} # special static files url_for if "." not in view_name: view_name = f"{self.name}.{view_name}" if view_name.endswith(".static"): name = kwargs.pop("name", None) if name: view_name = view_name.replace("static", name) kw.update(name=view_name) route = self.router.find_route_by_view_name(view_name, **kw) if not route: raise URLBuildError( f"Endpoint with name `{view_name}` was not found") uri = route.path if getattr(route.ctx, "static", None): filename = kwargs.pop("filename", "") # it's static folder if "__file_uri__" in uri: folder_ = uri.split("<__file_uri__:", 1)[0] if folder_.endswith("/"): folder_ = folder_[:-1] if filename.startswith("/"): filename = filename[1:] kwargs["__file_uri__"] = filename if (uri != "/" and uri.endswith("/") and not route.strict and not route.raw_path[:-1]): uri = uri[:-1] if not uri.startswith("/"): uri = f"/{uri}" out = uri # _method is only a placeholder now, don't know how to support it kwargs.pop("_method", None) anchor = kwargs.pop("_anchor", "") # _external need SERVER_NAME in config or pass _server arg host = kwargs.pop("_host", None) external = kwargs.pop("_external", False) or bool(host) scheme = kwargs.pop("_scheme", "") if route.ctx.hosts and external: if not host and len(route.ctx.hosts) > 1: raise ValueError( f"Host is ambiguous: {', '.join(route.ctx.hosts)}") elif host and host not in route.ctx.hosts: raise ValueError( f"Requested host ({host}) is not available for this " f"route: {route.ctx.hosts}") elif not host: host = list(route.ctx.hosts)[0] if scheme and not external: raise ValueError("When specifying _scheme, _external must be True") netloc = kwargs.pop("_server", None) if netloc is None and external: netloc = host or self.config.get("SERVER_NAME", "") if external: if not scheme: if ":" in netloc[:8]: scheme = netloc[:8].split(":", 1)[0] else: scheme = "http" if "://" in netloc[:8]: netloc = netloc.split("://", 1)[-1] # find all the parameters we will need to build in the URL # matched_params = re.findall(self.router.parameter_pattern, uri) route.finalize() for param_info in route.params.values(): # name, _type, pattern = self.router.parse_parameter_string(match) # we only want to match against each individual parameter try: supplied_param = str(kwargs.pop(param_info.name)) except KeyError: raise URLBuildError( f"Required parameter `{param_info.name}` was not " "passed to url_for") # determine if the parameter supplied by the caller # passes the test in the URL if param_info.pattern: passes_pattern = param_info.pattern.match(supplied_param) if not passes_pattern: if param_info.cast != str: msg = (f'Value "{supplied_param}" ' f"for parameter `{param_info.name}` does " "not match pattern for type " f"`{param_info.cast.__name__}`: " f"{param_info.pattern.pattern}") else: msg = (f'Value "{supplied_param}" for parameter ' f"`{param_info.name}` does not satisfy " f"pattern {param_info.pattern.pattern}") raise URLBuildError(msg) # replace the parameter in the URL with the supplied value replacement_regex = f"(<{param_info.name}.*?>)" out = re.sub(replacement_regex, supplied_param, out) # parse the remainder of the keyword arguments into a querystring query_string = urlencode(kwargs, doseq=True) if kwargs else "" # scheme://netloc/path;parameters?query#fragment out = urlunparse((scheme, netloc, out, "", query_string, anchor)) return out
def url_for(self, view_name: str, **kwargs): """Build a URL based on a view name and the values provided. In order to build a URL, all request parameters must be supplied as keyword arguments, and each parameter must pass the test for the specified parameter type. If these conditions are not met, a `URLBuildError` will be thrown. Keyword arguments that are not request parameters will be included in the output URL's query string. :param view_name: string referencing the view name :param \*\*kwargs: keys and values that are used to build request parameters and query string arguments. :return: the built URL Raises: URLBuildError """ # find the route by the supplied view name kw = {} # special static files url_for if view_name == 'static': kw.update(name=kwargs.pop('name', 'static')) elif view_name.endswith('.static'): # blueprint.static kwargs.pop('name', None) kw.update(name=view_name) uri, route = self.router.find_route_by_view_name(view_name, **kw) if not (uri and route): raise URLBuildError('Endpoint with name `{}` was not found'.format( view_name)) if view_name == 'static' or view_name.endswith('.static'): filename = kwargs.pop('filename', None) # it's static folder if '<file_uri:' in uri: folder_ = uri.split('<file_uri:', 1)[0] if folder_.endswith('/'): folder_ = folder_[:-1] if filename.startswith('/'): filename = filename[1:] uri = '{}/{}'.format(folder_, filename) if uri != '/' and uri.endswith('/'): uri = uri[:-1] out = uri # find all the parameters we will need to build in the URL matched_params = re.findall( self.router.parameter_pattern, uri) # _method is only a placeholder now, don't know how to support it kwargs.pop('_method', None) anchor = kwargs.pop('_anchor', '') # _external need SERVER_NAME in config or pass _server arg external = kwargs.pop('_external', False) scheme = kwargs.pop('_scheme', '') if scheme and not external: raise ValueError('When specifying _scheme, _external must be True') netloc = kwargs.pop('_server', None) if netloc is None and external: netloc = self.config.get('SERVER_NAME', '') if external: if not scheme: if ':' in netloc[:8]: scheme = netloc[:8].split(':', 1)[0] else: scheme = 'http' if '://' in netloc[:8]: netloc = netloc.split('://', 1)[-1] for match in matched_params: name, _type, pattern = self.router.parse_parameter_string( match) # we only want to match against each individual parameter specific_pattern = '^{}$'.format(pattern) supplied_param = None if name in kwargs: supplied_param = kwargs.get(name) del kwargs[name] else: raise URLBuildError( 'Required parameter `{}` was not passed to url_for'.format( name)) supplied_param = str(supplied_param) # determine if the parameter supplied by the caller passes the test # in the URL passes_pattern = re.match(specific_pattern, supplied_param) if not passes_pattern: if _type != str: msg = ( 'Value "{}" for parameter `{}` does not ' 'match pattern for type `{}`: {}'.format( supplied_param, name, _type.__name__, pattern)) else: msg = ( 'Value "{}" for parameter `{}` ' 'does not satisfy pattern {}'.format( supplied_param, name, pattern)) raise URLBuildError(msg) # replace the parameter in the URL with the supplied value replacement_regex = '(<{}.*?>)'.format(name) out = re.sub( replacement_regex, supplied_param, out) # parse the remainder of the keyword arguments into a querystring query_string = urlencode(kwargs, doseq=True) if kwargs else '' # scheme://netloc/path;parameters?query#fragment out = urlunparse((scheme, netloc, out, '', query_string, anchor)) return out
def url_for(self, view_name: str, **kwargs): r"""Build a URL based on a view name and the values provided. In order to build a URL, all request parameters must be supplied as keyword arguments, and each parameter must pass the test for the specified parameter type. If these conditions are not met, a `URLBuildError` will be thrown. Keyword arguments that are not request parameters will be included in the output URL's query string. :param view_name: string referencing the view name :param \**kwargs: keys and values that are used to build request parameters and query string arguments. :return: the built URL Raises: URLBuildError """ # find the route by the supplied view name kw: Dict[str, str] = {} # special static files url_for if view_name == "static": kw.update(name=kwargs.pop("name", "static")) elif view_name.endswith(".static"): # blueprint.static kwargs.pop("name", None) kw.update(name=view_name) uri, route = self.router.find_route_by_view_name(view_name, **kw) if not (uri and route): raise URLBuildError( f"Endpoint with name `{view_name}` was not found") # If the route has host defined, split that off # TODO: Retain netloc and path separately in Route objects host = uri.find("/") if host > 0: host, uri = uri[:host], uri[host:] else: host = None if view_name == "static" or view_name.endswith(".static"): filename = kwargs.pop("filename", None) # it's static folder if "<file_uri:" in uri: folder_ = uri.split("<file_uri:", 1)[0] if folder_.endswith("/"): folder_ = folder_[:-1] if filename.startswith("/"): filename = filename[1:] uri = f"{folder_}/{filename}" if uri != "/" and uri.endswith("/"): uri = uri[:-1] out = uri # find all the parameters we will need to build in the URL matched_params = re.findall(self.router.parameter_pattern, uri) # _method is only a placeholder now, don't know how to support it kwargs.pop("_method", None) anchor = kwargs.pop("_anchor", "") # _external need SERVER_NAME in config or pass _server arg external = kwargs.pop("_external", False) scheme = kwargs.pop("_scheme", "") if scheme and not external: raise ValueError("When specifying _scheme, _external must be True") netloc = kwargs.pop("_server", None) if netloc is None and external: netloc = host or self.config.get("SERVER_NAME", "") if external: if not scheme: if ":" in netloc[:8]: scheme = netloc[:8].split(":", 1)[0] else: scheme = "http" if "://" in netloc[:8]: netloc = netloc.split("://", 1)[-1] for match in matched_params: name, _type, pattern = self.router.parse_parameter_string(match) # we only want to match against each individual parameter specific_pattern = f"^{pattern}$" supplied_param = None if name in kwargs: supplied_param = kwargs.get(name) del kwargs[name] else: raise URLBuildError( f"Required parameter `{name}` was not passed to url_for") supplied_param = str(supplied_param) # determine if the parameter supplied by the caller passes the test # in the URL passes_pattern = re.match(specific_pattern, supplied_param) if not passes_pattern: if _type != str: type_name = _type.__name__ msg = (f'Value "{supplied_param}" ' f"for parameter `{name}` does not " f"match pattern for type `{type_name}`: {pattern}") else: msg = (f'Value "{supplied_param}" for parameter `{name}` ' f"does not satisfy pattern {pattern}") raise URLBuildError(msg) # replace the parameter in the URL with the supplied value replacement_regex = f"(<{name}.*?>)" out = re.sub(replacement_regex, supplied_param, out) # parse the remainder of the keyword arguments into a querystring query_string = urlencode(kwargs, doseq=True) if kwargs else "" # scheme://netloc/path;parameters?query#fragment out = urlunparse((scheme, netloc, out, "", query_string, anchor)) return out
def url_for( self, view_name: str, _anchor: str = "", _external: bool = False, _scheme: str = "", _server: str = None, _method: object = None, **kwargs ): # ? i think this should be in the Router uri, route = self.router.find_route_by_view_name(view_name) if not (uri and route): raise URLBuildError( "Endpoint with name `{}` was not found".format(view_name) ) if _scheme and not _external: raise ValueError("When specifying _scheme, _external must be True") if _server is None and _external: _server = self.config.get("SERVER_NAME", "") if _external: if not _scheme: if ":" in _server[:8]: _scheme = _server[:8].split(":", 1)[0] else: _scheme = "http" if "://" in _server[:8]: _server = _server.split("://", 1)[-1] orig_uri = uri replaced_vars = [] for k in kwargs: if re.search(r"([:|\*])", str(kwargs[k])): raise URLBuildError( "The parameter '{}' passed for URL `{}` with the value of " "'{}' may contain invalid characters that can break the " "URL".format(k, orig_uri, kwargs[k]) ) m = re.search(r"([:|\*]{})".format(k), uri) if m: replaced_vars.append(k) uri = uri.replace(m.group(0), str(kwargs[k])) # else log ? for k in replaced_vars: del kwargs[k] if uri.find(":") > -1 or uri.find("*") > -1: raise URLBuildError( "Required parameters for URL `{}` was not passed to " "url_for".format(orig_uri) ) # parse the remainder of the keyword arguments into a querystring query_string = urlencode(kwargs, doseq=True) if kwargs else "" # scheme://netloc/path;parameters?query#fragment return urlunparse((_scheme, _server, uri, "", query_string, _anchor))
def url_for(self, view_name: str, **kwargs): """Build a URL based on a view name and the values provided. In order to build a URL, all request parameters must be supplied as keyword arguments, and each parameter must pass the test for the specified parameter type. If these conditions are not met, a `URLBuildError` will be thrown. Keyword arguments that are not request parameters will be included in the output URL's query string. :param view_name: string referencing the view name :param **kwargs: keys and values that are used to build request parameters and query string arguments. :return: the built URL Raises: URLBuildError """ # find the route by the supplied view name uri, route = self.router.find_route_by_view_name(view_name) if not uri or not route: raise URLBuildError( 'Endpoint with name `{}` was not found'.format( view_name)) out = uri # find all the parameters we will need to build in the URL matched_params = re.findall( self.router.parameter_pattern, uri) # _method is only a placeholder now, don't know how to support it kwargs.pop('_method', None) anchor = kwargs.pop('_anchor', '') # _external need SERVER_NAME in config or pass _server arg external = kwargs.pop('_external', False) scheme = kwargs.pop('_scheme', '') if scheme and not external: raise ValueError('When specifying _scheme, _external must be True') netloc = kwargs.pop('_server', None) if netloc is None and external: netloc = self.config.get('SERVER_NAME', '') for match in matched_params: name, _type, pattern = self.router.parse_parameter_string( match) # we only want to match against each individual parameter specific_pattern = '^{}$'.format(pattern) supplied_param = None if kwargs.get(name): supplied_param = kwargs.get(name) del kwargs[name] else: raise URLBuildError( 'Required parameter `{}` was not passed to url_for'.format( name)) supplied_param = str(supplied_param) # determine if the parameter supplied by the caller passes the test # in the URL passes_pattern = re.match(specific_pattern, supplied_param) if not passes_pattern: if _type != str: msg = ( 'Value "{}" for parameter `{}` does not ' 'match pattern for type `{}`: {}'.format( supplied_param, name, _type.__name__, pattern)) else: msg = ( 'Value "{}" for parameter `{}` ' 'does not satisfy pattern {}'.format( supplied_param, name, pattern)) raise URLBuildError(msg) # replace the parameter in the URL with the supplied value replacement_regex = '(<{}.*?>)'.format(name) out = re.sub( replacement_regex, supplied_param, out) # parse the remainder of the keyword arguments into a querystring query_string = urlencode(kwargs, doseq=True) if kwargs else '' # scheme://netloc/path;parameters?query#fragment out = urlunparse((scheme, netloc, out, '', query_string, anchor)) return out