blob: 53de95ce6733e6459b9dcdaf2be0df2d28cdfed1 [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
18import pycodestyle
19
20# D701: Default parameter value is a mutable type
21# D702: Log messages require translation
22# D703: Found use of _() without explicit import of _!
23# D704: Found import of %s. This oslo library has been graduated!
24# D705: timeutils.utcnow() must be used instead of datetime.%s()
25# D706: Don't translate debug level logs
26# D707: basestring is not Python3-compatible, use str instead.
27# D708: Do not use xrange. Use range for large loops.
28# D709: LOG.audit is deprecated, please use LOG.info!
29# D710: LOG.warn() is not allowed. Use LOG.warning()
30# D711: Don't use backslashes for line continuation.
31
32UNDERSCORE_IMPORT_FILES = []
33
34
35mutable_default_argument_check = re.compile(
36 r"^\s*def .+\((.+=\{\}|.+=\[\])")
37string_translation = re.compile(r"[^_]*_\(\s*('|\")")
38translated_log = re.compile(
39 r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)"
40 r"\(\s*_\(\s*('|\")")
41underscore_import_check = re.compile(r"(.)*import _(.)*")
42# We need this for cases where they have created their own _ function.
43custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*")
44graduated_oslo_libraries_import_re = re.compile(
45 r"^\s*(?:import|from) designate\.openstack\.common\.?.*?"
46 r"(gettextutils|rpc)"
47 r".*?")
48no_line_continuation_backslash_re = re.compile(r'.*(\\)\n')
49
50
51@core.flake8ext
52def mutable_default_arguments(physical_line, logical_line, filename):
53 if pycodestyle.noqa(physical_line):
54 return
55
56 if mutable_default_argument_check.match(logical_line):
57 yield (0, "D701: Default parameter value is a mutable type")
58
59
60@core.flake8ext
61def no_translate_debug_logs(logical_line, filename):
62 """Check for 'LOG.debug(_('
63 As per our translation policy,
64 https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation
65 we shouldn't translate debug level logs.
66 * This check assumes that 'LOG' is a logger.
67 * Use filename so we can start enforcing this in specific folders instead
68 of needing to do so all at once.
69 N319
70 """
71 if logical_line.startswith("LOG.debug(_("):
72 yield(0, "D706: Don't translate debug level logs")
73
74
75@core.flake8ext
76def check_explicit_underscore_import(logical_line, filename):
77 """Check for explicit import of the _ function
78
79 We need to ensure that any files that are using the _() function
80 to translate logs are explicitly importing the _ function. We
81 can't trust unit test to catch whether the import has been
82 added so we need to check for it here.
83 """
84 # Build a list of the files that have _ imported. No further
85 # checking needed once it is found.
86 if filename in UNDERSCORE_IMPORT_FILES:
87 pass
88 elif (underscore_import_check.match(logical_line) or
89 custom_underscore_check.match(logical_line)):
90 UNDERSCORE_IMPORT_FILES.append(filename)
91 elif (translated_log.match(logical_line) or
92 string_translation.match(logical_line)):
93 yield(0, "D703: Found use of _() without explicit import of _!")
94
95
96@core.flake8ext
97def no_import_graduated_oslo_libraries(logical_line, filename):
98 """Check that we don't continue to use o.c. oslo libraries after graduation
99
100 After a library graduates from oslo-incubator, as we make the switch, we
101 should ensure we don't continue to use the oslo-incubator versions.
102
103 In many cases, it's not possible to immediately remove the code from the
104 openstack/common folder due to dependency issues.
105 """
106 # We can't modify oslo-incubator code, so ignore it here.
107 if "designate/openstack/common" in filename:
108 return
109
110 matches = graduated_oslo_libraries_import_re.match(logical_line)
111 if matches:
112 yield(0, "D704: Found import of %s. This oslo library has been "
113 "graduated!" % matches.group(1))
114
115
116@core.flake8ext
117def use_timeutils_utcnow(logical_line, filename):
118 # tools are OK to use the standard datetime module
119 if "/tools/" in filename:
120 return
121
122 msg = "D705: timeutils.utcnow() must be used instead of datetime.%s()"
123
124 datetime_funcs = ['now', 'utcnow']
125 for f in datetime_funcs:
126 pos = logical_line.find('datetime.%s' % f)
127 if pos != -1:
128 yield (pos, msg % f)
129
130
131@core.flake8ext
132def check_no_basestring(logical_line):
133 if re.search(r"\bbasestring\b", logical_line):
134 msg = ("D707: basestring is not Python3-compatible, use "
135 "str instead.")
136 yield(0, msg)
137
138
139@core.flake8ext
140def check_python3_xrange(logical_line):
141 if re.search(r"\bxrange\s*\(", logical_line):
142 yield(0, "D708: Do not use xrange. Use range for "
143 "large loops.")
144
145
146@core.flake8ext
147def check_no_log_audit(logical_line):
148 """Ensure that we are not using LOG.audit messages
149 Plans are in place going forward as discussed in the following
150 spec (https://review.opendev.org/#/c/132552/) to take out
151 LOG.audit messages. Given that audit was a concept invented
152 for OpenStack we can enforce not using it.
153 """
154 if "LOG.audit(" in logical_line:
155 yield(0, "D709: LOG.audit is deprecated, please use LOG.info!")
156
157
158@core.flake8ext
159def check_no_log_warn(logical_line):
160 """Disallow 'LOG.warn('
161
162 D710
163 """
164 if logical_line.startswith('LOG.warn('):
165 yield(0, "D710:Use LOG.warning() rather than LOG.warn()")
166
167
168@core.flake8ext
169def check_line_continuation_no_backslash(logical_line, tokens):
170 """D711 - Don't use backslashes for line continuation.
171
172 :param logical_line: The logical line to check. Not actually used.
173 :param tokens: List of tokens to check.
174 :returns: None if the tokens don't contain any issues, otherwise a tuple
175 is yielded that contains the offending index in the logical
176 line and a message describe the check validation failure.
177 """
178 backslash = None
179 for token_type, text, start, end, orig_line in tokens:
180 m = no_line_continuation_backslash_re.match(orig_line)
181 if m:
182 backslash = (start[0], m.start(1))
183 break
184
185 if backslash is not None:
186 msg = 'D711 Backslash line continuations not allowed'
187 yield backslash, msg