Plain is headed towards 1.0! Subscribe for development updates →

 1from __future__ import annotations
 2
 3from typing import Any
 4
 5from plain.urls import NoReverseMatch, reverse
 6from plain.utils.functional import Promise
 7
 8
 9def resolve_url(to: Any, *args: Any, **kwargs: Any) -> str:
10    """
11    Return a URL appropriate for the arguments passed.
12
13    The arguments could be:
14
15        * A model: the model's `get_absolute_url()` function will be called.
16
17        * A view name, possibly with arguments: `urls.reverse()` will be used
18          to reverse-resolve the name.
19
20        * A URL, which will be returned as-is.
21    """
22    # If it's a model, use get_absolute_url()
23    if hasattr(to, "get_absolute_url"):
24        return to.get_absolute_url()
25
26    if isinstance(to, Promise):
27        # Expand the lazy instance, as it can cause issues when it is passed
28        # further to some Python functions like urlparse.
29        to = str(to)
30
31    # Handle relative URLs
32    if isinstance(to, str) and to.startswith(("./", "../")):
33        return to
34
35    # Next try a reverse URL resolution.
36    try:
37        return reverse(to, *args, **kwargs)
38    except NoReverseMatch:
39        # If this is a callable, re-raise.
40        if callable(to):
41            raise
42        # If this doesn't "feel" like a URL, re-raise.
43        if "/" not in to and "." not in to:
44            raise
45
46    # Finally, fall back and assume it's a URL
47    return to