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)