1from collections.abc import Iterable
2from itertools import chain
3
4from django.utils.inspect import func_accepts_kwargs
5
6
7class Tags:
8 """
9 Built-in tags for internal checks.
10 """
11
12 admin = "admin"
13 async_support = "async_support"
14 caches = "caches"
15 commands = "commands"
16 compatibility = "compatibility"
17 database = "database"
18 files = "files"
19 models = "models"
20 security = "security"
21 signals = "signals"
22 sites = "sites"
23 staticfiles = "staticfiles"
24 templates = "templates"
25 translation = "translation"
26 urls = "urls"
27
28
29class CheckRegistry:
30 def __init__(self):
31 self.registered_checks = set()
32 self.deployment_checks = set()
33
34 def register(self, check=None, *tags, **kwargs):
35 """
36 Can be used as a function or a decorator. Register given function
37 `f` labeled with given `tags`. The function should receive **kwargs
38 and return list of Errors and Warnings.
39
40 Example::
41
42 registry = CheckRegistry()
43 @registry.register('mytag', 'anothertag')
44 def my_check(app_configs, **kwargs):
45 # ... perform checks and collect `errors` ...
46 return errors
47 # or
48 registry.register(my_check, 'mytag', 'anothertag')
49 """
50
51 def inner(check):
52 if not func_accepts_kwargs(check):
53 raise TypeError(
54 "Check functions must accept keyword arguments (**kwargs)."
55 )
56 check.tags = tags
57 checks = (
58 self.deployment_checks
59 if kwargs.get("deploy")
60 else self.registered_checks
61 )
62 checks.add(check)
63 return check
64
65 if callable(check):
66 return inner(check)
67 else:
68 if check:
69 tags += (check,)
70 return inner
71
72 def run_checks(
73 self,
74 app_configs=None,
75 tags=None,
76 include_deployment_checks=False,
77 databases=None,
78 ):
79 """
80 Run all registered checks and return list of Errors and Warnings.
81 """
82 errors = []
83 checks = self.get_checks(include_deployment_checks)
84
85 if tags is not None:
86 checks = [check for check in checks if not set(check.tags).isdisjoint(tags)]
87
88 for check in checks:
89 new_errors = check(app_configs=app_configs, databases=databases)
90 if not isinstance(new_errors, Iterable):
91 raise TypeError(
92 "The function %r did not return a list. All functions "
93 "registered with the checks registry must return a list." % check,
94 )
95 errors.extend(new_errors)
96 return errors
97
98 def tag_exists(self, tag, include_deployment_checks=False):
99 return tag in self.tags_available(include_deployment_checks)
100
101 def tags_available(self, deployment_checks=False):
102 return set(
103 chain.from_iterable(
104 check.tags for check in self.get_checks(deployment_checks)
105 )
106 )
107
108 def get_checks(self, include_deployment_checks=False):
109 checks = list(self.registered_checks)
110 if include_deployment_checks:
111 checks.extend(self.deployment_checks)
112 return checks
113
114
115registry = CheckRegistry()
116register = registry.register
117run_checks = registry.run_checks
118tag_exists = registry.tag_exists