1from __future__ import annotations
 2
 3from typing import Any
 4
 5from plain.runtime import settings
 6from plain.utils.functional import lazy
 7
 8from .exceptions import NoReverseMatch
 9from .resolvers import get_ns_resolver, get_resolver
10
11
12def reverse(url_name: str, *args: Any, **kwargs: Any) -> str:
13    resolver = get_resolver()
14
15    *path, view = url_name.split(":")
16
17    current_path = None
18
19    resolved_path = []
20    ns_pattern = ""
21    ns_converters = {}
22    for ns in path:
23        current_ns = current_path.pop() if current_path else None
24
25        if ns != current_ns:
26            current_path = None
27
28        try:
29            extra, resolver = resolver.namespace_dict[ns]
30            resolved_path.append(ns)
31            ns_pattern += extra
32            ns_converters.update(resolver.pattern.converters)
33        except KeyError as key:
34            if resolved_path:
35                raise NoReverseMatch(
36                    "{} is not a registered namespace inside '{}'".format(
37                        key, ":".join(resolved_path)
38                    )
39                )
40            else:
41                raise NoReverseMatch(f"{key} is not a registered namespace")
42    if ns_pattern:
43        resolver = get_ns_resolver(ns_pattern, resolver, tuple(ns_converters.items()))
44
45    return resolver.reverse(view, *args, **kwargs)
46
47
48reverse_lazy = lazy(reverse, str)
49
50
51def absolute_url(path: str) -> str:
52    """Convert a relative path to an absolute URL using the BASE_URL setting."""
53    if not settings.BASE_URL:
54        raise ValueError(
55            "The BASE_URL setting must be configured to generate absolute URLs."
56        )
57
58    base = settings.BASE_URL.rstrip("/")
59    if path and not path.startswith("/"):
60        path = "/" + path
61
62    return base + path
63
64
65def reverse_absolute(url_name: str, *args: Any, **kwargs: Any) -> str:
66    """Reverse a URL name and return an absolute URL using the BASE_URL setting."""
67    return absolute_url(reverse(url_name, *args, **kwargs))