Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/preflight/urls.py: 22%

1 from collections import Counter

2

3 from plain.runtime import settings

4

5 from . import Error, Warning, register

6

7

8@register

9 def check_url_config(package_configs, **kwargs):

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

11 from plain.urls import get_resolver

12

13 resolver = get_resolver()

14 return check_resolver(resolver)

15 return []

16

17

18 def check_resolver(resolver):

19 """

20 Recursively check the resolver.

21 """

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

23 if check_method is not None:

24 return check_method()

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

26 return get_warning_for_invalid_pattern(resolver)

27 else:

28 return []

29

30

31@register

32 def check_url_namespaces_unique(package_configs, **kwargs):

33 """

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

35 """

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

37 return []

38

39 from plain.urls import get_resolver

40

41 resolver = get_resolver()

42 all_namespaces = _load_all_namespaces(resolver)

43 counter = Counter(all_namespaces)

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

45 errors = []

46 for namespace in non_unique_namespaces:

47 errors.append(

48 Warning(

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

50 "all URLs in this namespace",

51 id="urls.W005",

52 )

53 )

54 return errors

55

56

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

58 """

59 Recursively load all namespaces from URL patterns.

60 """

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

62 namespaces = [

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

64 for url in url_patterns

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

66 ]

67 for pattern in url_patterns:

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

69 current = parents

70 if namespace is not None:

71 current += (namespace,)

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

73 return namespaces

74

75

76 def get_warning_for_invalid_pattern(pattern):

77 """

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

79

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

81 urlpattern having regex or name attributes.

82 """

83 if isinstance(pattern, str):

84 hint = (

85 f"Try removing the string '{pattern}'. The list of urlpatterns should not "

86 "have a prefix string as the first element."

87 )

88 elif isinstance(pattern, tuple):

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

90 else:

91 hint = None

92

93 return [

94 Error(

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

96 "of path() and/or re_path() instances.",

97 hint=hint,

98 id="urls.E004",

99 )

100 ]

101

102

103 def E006(name):

104 return Error(

105 f"The {name} setting must end with a slash.",

106 id="urls.E006",

107 )