blob: bce63ef590e57712020c0f42508c7b83fa542021 [file] [log] [blame]
Michael Johnsoncc8f89b2021-11-30 00:31:24 +00001# Copyright 2014 Hewlett-Packard Development Company, L.P.
2# Copyright (c) 2012, Cloudscaling
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15import re
16
17from hacking import core
Michael Johnsoncc8f89b2021-11-30 00:31:24 +000018
19# D701: Default parameter value is a mutable type
20# D702: Log messages require translation
21# D703: Found use of _() without explicit import of _!
22# D704: Found import of %s. This oslo library has been graduated!
23# D705: timeutils.utcnow() must be used instead of datetime.%s()
24# D706: Don't translate debug level logs
25# D707: basestring is not Python3-compatible, use str instead.
26# D708: Do not use xrange. Use range for large loops.
27# D709: LOG.audit is deprecated, please use LOG.info!
28# D710: LOG.warn() is not allowed. Use LOG.warning()
29# D711: Don't use backslashes for line continuation.
30
31UNDERSCORE_IMPORT_FILES = []
32
33
34mutable_default_argument_check = re.compile(
35 r"^\s*def .+\((.+=\{\}|.+=\[\])")
36string_translation = re.compile(r"[^_]*_\(\s*('|\")")
37translated_log = re.compile(
38 r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)"
39 r"\(\s*_\(\s*('|\")")
40underscore_import_check = re.compile(r"(.)*import _(.)*")
41# We need this for cases where they have created their own _ function.
42custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*")
43graduated_oslo_libraries_import_re = re.compile(
44 r"^\s*(?:import|from) designate\.openstack\.common\.?.*?"
45 r"(gettextutils|rpc)"
46 r".*?")
47no_line_continuation_backslash_re = re.compile(r'.*(\\)\n')
48
49
50@core.flake8ext
Michael Johnson4d257b32024-11-08 20:28:36 +000051def mutable_default_arguments(logical_line, filename, noqa):
52 if noqa:
Michael Johnsoncc8f89b2021-11-30 00:31:24 +000053 return
54
55 if mutable_default_argument_check.match(logical_line):
56 yield (0, "D701: Default parameter value is a mutable type")
57
58
59@core.flake8ext
60def no_translate_debug_logs(logical_line, filename):
61 """Check for 'LOG.debug(_('
62 As per our translation policy,
63 https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation
64 we shouldn't translate debug level logs.
65 * This check assumes that 'LOG' is a logger.
66 * Use filename so we can start enforcing this in specific folders instead
67 of needing to do so all at once.
68 N319
69 """
70 if logical_line.startswith("LOG.debug(_("):
Michael Johnson4d257b32024-11-08 20:28:36 +000071 yield (0, "D706: Don't translate debug level logs")
Michael Johnsoncc8f89b2021-11-30 00:31:24 +000072
73
74@core.flake8ext
75def check_explicit_underscore_import(logical_line, filename):
76 """Check for explicit import of the _ function
77
78 We need to ensure that any files that are using the _() function
79 to translate logs are explicitly importing the _ function. We
80 can't trust unit test to catch whether the import has been
81 added so we need to check for it here.
82 """
83 # Build a list of the files that have _ imported. No further
84 # checking needed once it is found.
85 if filename in UNDERSCORE_IMPORT_FILES:
86 pass
87 elif (underscore_import_check.match(logical_line) or
88 custom_underscore_check.match(logical_line)):
89 UNDERSCORE_IMPORT_FILES.append(filename)
90 elif (translated_log.match(logical_line) or
91 string_translation.match(logical_line)):
Michael Johnson4d257b32024-11-08 20:28:36 +000092 yield (0, "D703: Found use of _() without explicit import of _!")
Michael Johnsoncc8f89b2021-11-30 00:31:24 +000093
94
95@core.flake8ext
96def no_import_graduated_oslo_libraries(logical_line, filename):
97 """Check that we don't continue to use o.c. oslo libraries after graduation
98
99 After a library graduates from oslo-incubator, as we make the switch, we
100 should ensure we don't continue to use the oslo-incubator versions.
101
102 In many cases, it's not possible to immediately remove the code from the
103 openstack/common folder due to dependency issues.
104 """
105 # We can't modify oslo-incubator code, so ignore it here.
106 if "designate/openstack/common" in filename:
107 return
108
109 matches = graduated_oslo_libraries_import_re.match(logical_line)
110 if matches:
Michael Johnson4d257b32024-11-08 20:28:36 +0000111 yield (0, "D704: Found import of %s. This oslo library has been "
112 "graduated!" % matches.group(1))
Michael Johnsoncc8f89b2021-11-30 00:31:24 +0000113
114
115@core.flake8ext
116def use_timeutils_utcnow(logical_line, filename):
117 # tools are OK to use the standard datetime module
118 if "/tools/" in filename:
119 return
120
121 msg = "D705: timeutils.utcnow() must be used instead of datetime.%s()"
122
123 datetime_funcs = ['now', 'utcnow']
124 for f in datetime_funcs:
125 pos = logical_line.find('datetime.%s' % f)
126 if pos != -1:
127 yield (pos, msg % f)
128
129
130@core.flake8ext
131def check_no_basestring(logical_line):
132 if re.search(r"\bbasestring\b", logical_line):
133 msg = ("D707: basestring is not Python3-compatible, use "
134 "str instead.")
Michael Johnson4d257b32024-11-08 20:28:36 +0000135 yield (0, msg)
Michael Johnsoncc8f89b2021-11-30 00:31:24 +0000136
137
138@core.flake8ext
139def check_python3_xrange(logical_line):
140 if re.search(r"\bxrange\s*\(", logical_line):
Michael Johnson4d257b32024-11-08 20:28:36 +0000141 yield (0, "D708: Do not use xrange. Use range for large loops.")
Michael Johnsoncc8f89b2021-11-30 00:31:24 +0000142
143
144@core.flake8ext
145def check_no_log_audit(logical_line):
146 """Ensure that we are not using LOG.audit messages
147 Plans are in place going forward as discussed in the following
148 spec (https://review.opendev.org/#/c/132552/) to take out
149 LOG.audit messages. Given that audit was a concept invented
150 for OpenStack we can enforce not using it.
151 """
152 if "LOG.audit(" in logical_line:
Michael Johnson4d257b32024-11-08 20:28:36 +0000153 yield (0, "D709: LOG.audit is deprecated, please use LOG.info!")
Michael Johnsoncc8f89b2021-11-30 00:31:24 +0000154
155
156@core.flake8ext
157def check_no_log_warn(logical_line):
158 """Disallow 'LOG.warn('
159
160 D710
161 """
162 if logical_line.startswith('LOG.warn('):
Michael Johnson4d257b32024-11-08 20:28:36 +0000163 yield (0, "D710:Use LOG.warning() rather than LOG.warn()")
Michael Johnsoncc8f89b2021-11-30 00:31:24 +0000164
165
166@core.flake8ext
167def check_line_continuation_no_backslash(logical_line, tokens):
168 """D711 - Don't use backslashes for line continuation.
169
170 :param logical_line: The logical line to check. Not actually used.
171 :param tokens: List of tokens to check.
172 :returns: None if the tokens don't contain any issues, otherwise a tuple
173 is yielded that contains the offending index in the logical
174 line and a message describe the check validation failure.
175 """
176 backslash = None
177 for token_type, text, start, end, orig_line in tokens:
178 m = no_line_continuation_backslash_re.match(orig_line)
179 if m:
180 backslash = (start[0], m.start(1))
181 break
182
183 if backslash is not None:
184 msg = 'D711 Backslash line continuations not allowed'
185 yield backslash, msg