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