Plain is headed towards 1.0! Subscribe for development updates →

urls

Route requests to views.

 1"""Functions for use in URLsconfs."""
 2
 3from functools import partial
 4
 5from plain.exceptions import ImproperlyConfigured
 6
 7from .resolvers import (
 8    RegexPattern,
 9    RoutePattern,
10    URLPattern,
11    URLResolver,
12)
13
14
15def include(arg, namespace=None):
16    default_namespace = None
17    if isinstance(arg, tuple):
18        # Callable returning a namespace hint.
19        try:
20            urlconf_module, default_namespace = arg
21        except ValueError:
22            if namespace:
23                raise ImproperlyConfigured(
24                    "Cannot override the namespace for a dynamic module that "
25                    "provides a namespace."
26                )
27            raise ImproperlyConfigured(
28                "Passing a %d-tuple to include() is not supported. Pass a "  # noqa: UP031
29                "2-tuple containing the list of patterns and default_namespace, and "
30                "provide the namespace argument to include() instead." % len(arg)
31            )
32    else:
33        # No namespace hint - use manually provided namespace.
34        urlconf_module = arg
35
36    patterns = getattr(urlconf_module, "urlpatterns", urlconf_module)
37    default_namespace = getattr(urlconf_module, "default_namespace", default_namespace)
38    if namespace and not default_namespace:
39        raise ImproperlyConfigured(
40            "Specifying a namespace in include() without providing an default_namespace "
41            "is not supported. Set the default_namespace attribute in the included "
42            "module, or pass a 2-tuple containing the list of patterns and "
43            "default_namespace instead.",
44        )
45    namespace = namespace or default_namespace
46    # Make sure the patterns can be iterated through (without this, some
47    # testcases will break).
48    if isinstance(patterns, list | tuple):
49        for url_pattern in patterns:
50            getattr(url_pattern, "pattern", None)
51    return (urlconf_module, default_namespace, namespace)
52
53
54def _path(route, view, kwargs=None, name=None, Pattern=None):
55    from plain.views import View
56
57    if kwargs is not None and not isinstance(kwargs, dict):
58        raise TypeError(
59            f"kwargs argument must be a dict, but got {kwargs.__class__.__name__}."
60        )
61
62    if isinstance(view, list | tuple):
63        # For include(...) processing.
64        pattern = Pattern(route, is_endpoint=False)
65        urlconf_module, default_namespace, namespace = view
66        return URLResolver(
67            pattern,
68            urlconf_module,
69            kwargs,
70            default_namespace=default_namespace,
71            namespace=namespace,
72        )
73
74    if isinstance(view, View):
75        view_cls_name = view.__class__.__name__
76        raise TypeError(
77            f"view must be a callable, pass {view_cls_name} or {view_cls_name}.as_view(*args, **kwargs), not "
78            f"{view_cls_name}()."
79        )
80
81    # Automatically call view.as_view() for class-based views
82    if as_view := getattr(view, "as_view", None):
83        pattern = Pattern(route, name=name, is_endpoint=True)
84        return URLPattern(pattern, as_view(), kwargs, name)
85
86    # Function-based views or view_class.as_view() usage
87    if callable(view):
88        pattern = Pattern(route, name=name, is_endpoint=True)
89        return URLPattern(pattern, view, kwargs, name)
90
91    raise TypeError("view must be a callable or a list/tuple in the case of include().")
92
93
94path = partial(_path, Pattern=RoutePattern)
95re_path = partial(_path, Pattern=RegexPattern)