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)