Plain is headed towards 1.0! Subscribe for development updates →

urls

Route requests to views.

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