Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/urls/base.py: 69%

1 from threading import local

2

3 from plain.utils.functional import lazy

4

5 from .exceptions import NoReverseMatch, Resolver404

6 from .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

12 def resolve(path, urlconf=None):

13 if urlconf is None:

14 urlconf = get_urlconf()

15 return get_resolver(urlconf).resolve(path)

16

17

18 def 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(f"{key} is not a registered namespace")

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

81 reverse_lazy = lazy(reverse, str)

82

83

84 def clear_url_caches():

85 _get_cached_resolver.cache_clear()

86 get_ns_resolver.cache_clear()

87

88

89 def 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

101 def 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

109 def 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