Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/core/checks/urls.py: 21%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

82 statements  

1import inspect 

2from collections import Counter 

3 

4from django.conf import settings 

5from django.core.exceptions import ViewDoesNotExist 

6 

7from . import Error, Tags, Warning, register 

8 

9 

10@register(Tags.urls) 

11def check_url_config(app_configs, **kwargs): 

12 if getattr(settings, "ROOT_URLCONF", None): 

13 from django.urls import get_resolver 

14 

15 resolver = get_resolver() 

16 return check_resolver(resolver) 

17 return [] 

18 

19 

20def check_resolver(resolver): 

21 """ 

22 Recursively check the resolver. 

23 """ 

24 check_method = getattr(resolver, "check", None) 

25 if check_method is not None: 

26 return check_method() 

27 elif not hasattr(resolver, "resolve"): 

28 return get_warning_for_invalid_pattern(resolver) 

29 else: 

30 return [] 

31 

32 

33@register(Tags.urls) 

34def check_url_namespaces_unique(app_configs, **kwargs): 

35 """ 

36 Warn if URL namespaces used in applications aren't unique. 

37 """ 

38 if not getattr(settings, "ROOT_URLCONF", None): 

39 return [] 

40 

41 from django.urls import get_resolver 

42 

43 resolver = get_resolver() 

44 all_namespaces = _load_all_namespaces(resolver) 

45 counter = Counter(all_namespaces) 

46 non_unique_namespaces = [n for n, count in counter.items() if count > 1] 

47 errors = [] 

48 for namespace in non_unique_namespaces: 

49 errors.append( 

50 Warning( 

51 "URL namespace '{}' isn't unique. You may not be able to reverse " 

52 "all URLs in this namespace".format(namespace), 

53 id="urls.W005", 

54 ) 

55 ) 

56 return errors 

57 

58 

59def _load_all_namespaces(resolver, parents=()): 

60 """ 

61 Recursively load all namespaces from URL patterns. 

62 """ 

63 url_patterns = getattr(resolver, "url_patterns", []) 

64 namespaces = [ 

65 ":".join(parents + (url.namespace,)) 

66 for url in url_patterns 

67 if getattr(url, "namespace", None) is not None 

68 ] 

69 for pattern in url_patterns: 

70 namespace = getattr(pattern, "namespace", None) 

71 current = parents 

72 if namespace is not None: 

73 current += (namespace,) 

74 namespaces.extend(_load_all_namespaces(pattern, current)) 

75 return namespaces 

76 

77 

78def get_warning_for_invalid_pattern(pattern): 

79 """ 

80 Return a list containing a warning that the pattern is invalid. 

81 

82 describe_pattern() cannot be used here, because we cannot rely on the 

83 urlpattern having regex or name attributes. 

84 """ 

85 if isinstance(pattern, str): 

86 hint = ( 

87 "Try removing the string '{}'. The list of urlpatterns should not " 

88 "have a prefix string as the first element.".format(pattern) 

89 ) 

90 elif isinstance(pattern, tuple): 

91 hint = "Try using path() instead of a tuple." 

92 else: 

93 hint = None 

94 

95 return [ 

96 Error( 

97 "Your URL pattern {!r} is invalid. Ensure that urlpatterns is a list " 

98 "of path() and/or re_path() instances.".format(pattern), 

99 hint=hint, 

100 id="urls.E004", 

101 ) 

102 ] 

103 

104 

105@register(Tags.urls) 

106def check_url_settings(app_configs, **kwargs): 

107 errors = [] 

108 for name in ("STATIC_URL", "MEDIA_URL"): 

109 value = getattr(settings, name) 

110 if value and not value.endswith("/"): 

111 errors.append(E006(name)) 

112 return errors 

113 

114 

115def E006(name): 

116 return Error( 

117 "The {} setting must end with a slash.".format(name), 

118 id="urls.E006", 

119 ) 

120 

121 

122@register(Tags.urls) 

123def check_custom_error_handlers(app_configs, **kwargs): 

124 if not getattr(settings, "ROOT_URLCONF", None): 

125 return [] 

126 

127 from django.urls import get_resolver 

128 

129 resolver = get_resolver() 

130 

131 errors = [] 

132 # All handlers take (request, exception) arguments except handler500 

133 # which takes (request). 

134 for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]: 

135 try: 

136 handler = resolver.resolve_error_handler(status_code) 

137 except (ImportError, ViewDoesNotExist) as e: 

138 path = getattr(resolver.urlconf_module, "handler%s" % status_code) 

139 msg = ( 

140 "The custom handler{status_code} view '{path}' could not be " 

141 "imported." 

142 ).format(status_code=status_code, path=path) 

143 errors.append(Error(msg, hint=str(e), id="urls.E008")) 

144 continue 

145 signature = inspect.signature(handler) 

146 args = [None] * num_parameters 

147 try: 

148 signature.bind(*args) 

149 except TypeError: 

150 msg = ( 

151 "The custom handler{status_code} view '{path}' does not " 

152 "take the correct number of arguments ({args})." 

153 ).format( 

154 status_code=status_code, 

155 path=handler.__module__ + "." + handler.__qualname__, 

156 args="request, exception" if num_parameters == 2 else "request", 

157 ) 

158 errors.append(Error(msg, id="urls.E007")) 

159 return errors