Plain is headed towards 1.0! Subscribe for development updates →

urls

Route requests to views.

  1from threading import local
  2
  3from plain.utils.functional import lazy
  4
  5from .exceptions import NoReverseMatch, Resolver404
  6from .resolvers import _get_cached_resolver, get_ns_resolver, get_resolver
  7
  8# Overridden URLconfs for each thread are stored here.
  9_urlconfs = local()
 10
 11
 12def resolve(path, urlconf=None):
 13    if urlconf is None:
 14        urlconf = get_urlconf()
 15    return get_resolver(urlconf).resolve(path)
 16
 17
 18def reverse(viewname, urlconf=None, args=None, kwargs=None, using_namespace=None):
 19    if urlconf is None:
 20        urlconf = get_urlconf()
 21    resolver = get_resolver(urlconf)
 22    args = args or []
 23    kwargs = kwargs or {}
 24
 25    if not isinstance(viewname, str):
 26        view = viewname
 27    else:
 28        *path, view = viewname.split(":")
 29
 30        if using_namespace:
 31            current_path = using_namespace.split(":")
 32            current_path.reverse()
 33        else:
 34            current_path = None
 35
 36        resolved_path = []
 37        ns_pattern = ""
 38        ns_converters = {}
 39        for ns in path:
 40            current_ns = current_path.pop() if current_path else None
 41            # Lookup the name to see if it could be an app identifier.
 42            try:
 43                app_list = resolver.app_dict[ns]
 44                # Yes! Path part matches an app in the current Resolver.
 45                if current_ns and current_ns in app_list:
 46                    # If we are reversing for a particular app, use that
 47                    # namespace.
 48                    ns = current_ns
 49                elif ns not in app_list:
 50                    # The name isn't shared by one of the instances (i.e.,
 51                    # the default) so pick the first instance as the default.
 52                    ns = app_list[0]
 53            except KeyError:
 54                pass
 55
 56            if ns != current_ns:
 57                current_path = None
 58
 59            try:
 60                extra, resolver = resolver.namespace_dict[ns]
 61                resolved_path.append(ns)
 62                ns_pattern += extra
 63                ns_converters.update(resolver.pattern.converters)
 64            except KeyError as key:
 65                if resolved_path:
 66                    raise NoReverseMatch(
 67                        "{} is not a registered namespace inside '{}'".format(
 68                            key, ":".join(resolved_path)
 69                        )
 70                    )
 71                else:
 72                    raise NoReverseMatch("%s is not a registered namespace" % key)
 73        if ns_pattern:
 74            resolver = get_ns_resolver(
 75                ns_pattern, resolver, tuple(ns_converters.items())
 76            )
 77
 78    return resolver.reverse(view, *args, **kwargs)
 79
 80
 81reverse_lazy = lazy(reverse, str)
 82
 83
 84def clear_url_caches():
 85    _get_cached_resolver.cache_clear()
 86    get_ns_resolver.cache_clear()
 87
 88
 89def set_urlconf(urlconf_name):
 90    """
 91    Set the URLconf for the current thread (overriding the default one in
 92    settings). If urlconf_name is None, revert back to the default.
 93    """
 94    if urlconf_name:
 95        _urlconfs.value = urlconf_name
 96    else:
 97        if hasattr(_urlconfs, "value"):
 98            del _urlconfs.value
 99
100
101def get_urlconf(default=None):
102    """
103    Return the root URLconf to use for the current thread if it has been
104    changed from the default one.
105    """
106    return getattr(_urlconfs, "value", default)
107
108
109def is_valid_path(path, urlconf=None):
110    """
111    Return the ResolverMatch if the given path resolves against the default URL
112    resolver, False otherwise. This is a convenience method to make working
113    with "is this a match?" cases easier, avoiding try...except blocks.
114    """
115    try:
116        return resolve(path, urlconf)
117    except Resolver404:
118        return False