summaryrefslogtreecommitdiff
path: root/designateclient/hacking/checks.py
blob: e2a5410adb0a3316ed8fb2a2cf6e4b2ab99568f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2012, Cloudscaling
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import re

from hacking import core
import pycodestyle

# D701: Default parameter value is a mutable type
# D702: Log messages require translation
# D703: Found use of _() without explicit import of _!
# D704: Found import of %s. This oslo library has been graduated!
# D705: timeutils.utcnow() must be used instead of datetime.%s()
# D706: Don't translate debug level logs
# D707: basestring is not Python3-compatible, use str instead.
# D708: Do not use xrange. Use range for large loops.
# D709: LOG.audit is deprecated, please use LOG.info!
# D710: LOG.warn() is not allowed. Use LOG.warning()

UNDERSCORE_IMPORT_FILES = []


mutable_default_argument_check = re.compile(
    r"^\s*def .+\((.+=\{\}|.+=\[\])")
string_translation = re.compile(r"[^_]*_\(\s*('|\")")
translated_log = re.compile(
    r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)"
    r"\(\s*_\(\s*('|\")")
underscore_import_check = re.compile(r"(.)*import _(.)*")
# We need this for cases where they have created their own _ function.
custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*")
graduated_oslo_libraries_import_re = re.compile(
    r"^\s*(?:import|from) designate\.openstack\.common\.?.*?"
    r"(gettextutils|rpc)"
    r".*?")


@core.flake8ext
def mutable_default_arguments(physical_line, logical_line, filename):
    if pycodestyle.noqa(physical_line):
        return

    if mutable_default_argument_check.match(logical_line):
        yield (0, "D701: Default parameter value is a mutable type")


@core.flake8ext
def no_translate_debug_logs(logical_line, filename):
    """Check for 'LOG.debug(_('
    As per our translation policy,
    https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation
    we shouldn't translate debug level logs.
    * This check assumes that 'LOG' is a logger.
    * Use filename so we can start enforcing this in specific folders instead
      of needing to do so all at once.
    N319
    """
    if logical_line.startswith("LOG.debug(_("):
        yield(0, "D706: Don't translate debug level logs")


@core.flake8ext
def check_explicit_underscore_import(logical_line, filename):
    """Check for explicit import of the _ function

    We need to ensure that any files that are using the _() function
    to translate logs are explicitly importing the _ function.  We
    can't trust unit test to catch whether the import has been
    added so we need to check for it here.
    """
    # Build a list of the files that have _ imported.  No further
    # checking needed once it is found.
    if filename in UNDERSCORE_IMPORT_FILES:
        pass
    elif (underscore_import_check.match(logical_line) or
          custom_underscore_check.match(logical_line)):
        UNDERSCORE_IMPORT_FILES.append(filename)
    elif (translated_log.match(logical_line) or
          string_translation.match(logical_line)):
        yield(0, "D703: Found use of _() without explicit import of _!")


@core.flake8ext
def no_import_graduated_oslo_libraries(logical_line, filename):
    """Check that we don't continue to use o.c. oslo libraries after graduation

    After a library graduates from oslo-incubator, as we make the switch, we
    should ensure we don't continue to use the oslo-incubator versions.

    In many cases, it's not possible to immediately remove the code from the
    openstack/common folder due to dependency issues.
    """
    # We can't modify oslo-incubator code, so ignore it here.
    if "designate/openstack/common" in filename:
        return

    matches = graduated_oslo_libraries_import_re.match(logical_line)
    if matches:
        yield(0, "D704: Found import of %s. This oslo library has been "
                 "graduated!" % matches.group(1))


@core.flake8ext
def use_timeutils_utcnow(logical_line, filename):
    # tools are OK to use the standard datetime module
    if "/tools/" in filename:
        return

    msg = "D705: timeutils.utcnow() must be used instead of datetime.%s()"

    datetime_funcs = ['now', 'utcnow']
    for f in datetime_funcs:
        pos = logical_line.find('datetime.%s' % f)
        if pos != -1:
            yield (pos, msg % f)


@core.flake8ext
def check_no_basestring(logical_line):
    if re.search(r"\bbasestring\b", logical_line):
        msg = ("D707: basestring is not Python3-compatible, use "
               "str instead.")
        yield(0, msg)


@core.flake8ext
def check_python3_xrange(logical_line):
    if re.search(r"\bxrange\s*\(", logical_line):
        yield(0, "D708: Do not use xrange. Use range for "
                 "large loops.")


@core.flake8ext
def check_no_log_audit(logical_line):
    """Ensure that we are not using LOG.audit messages
    Plans are in place going forward as discussed in the following
    spec (https://review.opendev.org/#/c/132552/) to take out
    LOG.audit messages. Given that audit was a concept invented
    for OpenStack we can enforce not using it.
    """
    if "LOG.audit(" in logical_line:
        yield(0, "D709: LOG.audit is deprecated, please use LOG.info!")


@core.flake8ext
def check_no_log_warn(logical_line):
    """Disallow 'LOG.warn('

    D710
    """
    if logical_line.startswith('LOG.warn('):
        yield(0, "D710:Use LOG.warning() rather than LOG.warn()")