def check_oslo_namespace_imports(logical_line, physical_line, filename): if pep8.noqa(physical_line): return if re.match(oslo_namespace_imports, logical_line): msg = ("M333: '%s' must be used instead of '%s'.") % ( logical_line.replace('oslo.', 'oslo_'), logical_line) yield (0, msg)
def hacking_import_groups_together(logical_line, blank_lines, indent_level, previous_indent_level, line_number, physical_line, filename): r"""Check that like imports are grouped together. OpenStack HACKING guide recommendation for imports: Imports should be grouped together by type. Okay: import os\nimport sys Okay: try:\n import foo\nexcept ImportError:\n pass\n\nimport six H307: import os\n\nimport sys """ if line_number == 1 or filename != together_data.current_filename: together_data.current_group = None together_data.current_filename = filename if pep8.noqa(physical_line): return normalized_line = import_normalize(logical_line.strip()).split() if normalized_line and normalized_line[0] == 'import': current_type = _get_import_type(normalized_line[1]) previous_import = together_data.current_import together_data.current_import = normalized_line[1] matched = current_type == together_data.current_group together_data.current_group = current_type if (matched and indent_level == previous_indent_level and blank_lines >= 1): yield ( 0, 'H307: like imports should be grouped together (%s and ' '%s from %s are separated by whitespace)' % (previous_import, together_data.current_import, current_type))
def hacking_import_groups(logical_line, blank_lines, previous_logical, indent_level, previous_indent_level, physical_line): r"""Check that imports are grouped correctly. OpenStack HACKING guide recommendation for imports: imports grouped such that Python standard library imports are together, third party library imports are together, and project imports are together Okay: import os\nimport sys\n\nimport six\n\nimport hacking Okay: import six\nimport znon_existent_package H305: import hacking\nimport os H305: import os\nimport six H305: import os\nimport znon_existent_package """ if (pep8.noqa(physical_line) or blank_lines > 0 or indent_level != previous_indent_level): return normalized_line = import_normalize(logical_line.strip()).split() normalized_previous = import_normalize(previous_logical.strip()).split() if normalized_line and normalized_line[0] == 'import': current_type = _get_import_type(normalized_line[1]) if normalized_previous and normalized_previous[0] == 'import': previous_type = _get_import_type(normalized_previous[1]) if current_type != previous_type: yield (0, 'H305: imports not grouped correctly ' '(%s: %s, %s: %s)' % (normalized_previous[1], previous_type, normalized_line[1], current_type))
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory # and the Xen utilities if ("nova/tests" in filename or "plugins/xenserver/xenapi/etc/xapi.d" in filename or # TODO(Mike_D):Needs to be remove with: # I075ab2a522272f2082c292dfedc877abd8ebe328 "nova/virt/libvirt" in filename): return if pep8.noqa(physical_line): return msg = "N328: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "N329: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "N330: LOG.warning messages require translations `_LW()`!" if log_translation_LW.match(logical_line): yield (0, msg) msg = "N331: Use LOG.warning due to compatibility with py3" if log_warn.match(logical_line): yield (0, msg) msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def hacking_import_groups(logical_line, blank_lines, previous_logical, indent_level, previous_indent_level, physical_line): r"""Check that imports are grouped correctly. OpenStack HACKING guide recommendation for imports: imports grouped such that Python standard library imports are together, third party library imports are together, and project imports are together Okay: import os\nimport sys\n\nimport six\n\nimport hacking Okay: import six\nimport znon_existent_package H305: import hacking\nimport os H305: import os\nimport six H305: import os\nimport znon_existent_package """ if (pep8.noqa(physical_line) or blank_lines > 0 or indent_level != previous_indent_level): return normalized_line = import_normalize(logical_line.strip()).split() normalized_previous = import_normalize(previous_logical.strip()).split() if normalized_line and normalized_line[0] == 'import': current_type = _get_import_type(normalized_line[1]) if normalized_previous and normalized_previous[0] == 'import': previous_type = _get_import_type(normalized_previous[1]) if current_type != previous_type: yield(0, 'H305: imports not grouped correctly ' '(%s: %s, %s: %s)' % (normalized_previous[1], previous_type, normalized_line[1], current_type))
def hacking_import_groups_together(logical_line, blank_lines, indent_level, previous_indent_level, line_number, physical_line, filename): r"""Check that like imports are grouped together. OpenStack HACKING guide recommendation for imports: Imports should be grouped together by type. Okay: import os\nimport sys Okay: try:\n import foo\nexcept ImportError:\n pass\n\nimport six H307: import os\n\nimport sys """ if line_number == 1 or filename != together_data.current_filename: together_data.current_group = None together_data.current_filename = filename if pep8.noqa(physical_line): return normalized_line = import_normalize(logical_line.strip()).split() if normalized_line and normalized_line[0] == 'import': current_type = _get_import_type(normalized_line[1]) previous_import = together_data.current_import together_data.current_import = normalized_line[1] matched = current_type == together_data.current_group together_data.current_group = current_type if (matched and indent_level == previous_indent_level and blank_lines >= 1): yield(0, 'H307: like imports should be grouped together (%s and ' '%s from %s are separated by whitespace)' % (previous_import, together_data.current_import, current_type))
def check_todo_notes(physical_line: str): """Note: Do not forget that this `todo` note still needs to be finished""" if pep8.noqa(physical_line): return None _match = REGEX_NOTE.search(physical_line) if _match: return _match.start(), r'T000 : Todo note found'
def error(self, line_number, offset, text, check): if len(self.lines) > line_number - 1 and noqa( self.lines[line_number - 1]): return else: return super(RespectNoqaReport, self).error(line_number, offset, text, check)
def check_http_not_implemented(logical_line, physical_line, filename): msg = "N339: HTTPNotImplemented response must be implemented with" " common raise_feature_not_supported()." if pep8.noqa(physical_line): return if "nova/api/openstack/compute/legacy_v2" in filename or "nova/api/openstack/compute" not in filename: return if re.match(http_not_implemented_re, logical_line): yield (0, msg)
def check_oslo_namespace_imports(logical_line, physical_line, filename): if pep8.noqa(physical_line): return if re.match(oslo_namespace_imports, logical_line): msg = ("M333: '%s' must be used instead of '%s'.") % ( logical_line.replace('oslo.', 'oslo_'), logical_line) yield(0, msg)
def no_setupclass_for_unit_tests(physical_line, filename): if pep8.noqa(physical_line): return if 'tempest/tests' in filename: if SETUPCLASS_DEFINITION.match(physical_line): return (physical_line.find('def'), "T105: setUpClass can not be used with unit tests")
def check_http_not_implemented(logical_line, physical_line, filename): msg = ("N339: HTTPNotImplemented response must be implemented with" " common raise_feature_not_supported().") if pep8.noqa(physical_line): return if ("nova/api/openstack/compute" not in filename): return if re.match(http_not_implemented_re, logical_line): yield (0, msg)
def no_setup_teardown_class_for_tests(physical_line, filename): if pep8.noqa(physical_line): return if 'tempest/test.py' not in filename: if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line): return (physical_line.find('def'), "T105: (setUp|tearDown)Class can not be used in tests")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory if "neutron/tests" in filename: return if pep8.noqa(physical_line): return msg = "N320: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory # and the Xen utilities if "nova/tests" in filename or "plugins/xenserver/xenapi/etc/xapi.d" in filename: return if pep8.noqa(physical_line): return msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def check_regex_patterns(physical_line): if PATTERNS_CONFIG: if pep8.noqa(physical_line): return for pattern_cfg in PATTERNS_CONFIG: match = pattern_cfg['regex'].search(physical_line) if match: return (match.start(), str.format('R{} {}', pattern_cfg['code'], pattern_cfg['message']))
def run(self): if self.snippets: regex = re.compile(r'{}'.format(u'|'.join(self.snippets))) for index, line in enumerate(self.lines): if pep8.noqa(line): return match = regex.search(line) if match: text = 'S100 Snippet found: "{}".'.format(match.group()) yield index + 1, match.start(), text, type(self)
def no_setup_teardown_class_for_tests(physical_line, filename): """Check that tests do not use setUpClass/tearDownClass T105: Tests cannot use setUpClass/tearDownClass """ if pep8.noqa(physical_line): return if SETUP_TEARDOWN_CLASS_DEFINITION.match(physical_line): return (physical_line.find('def'), "T105: (setUp|tearDown)Class can not be used in tests")
def use_jsonutils(logical_line, filename): """Check to prevent importing json in sahara code. S375 """ if pep8.noqa(logical_line): return if (RE_USE_JSONUTILS_INVALID_LINE.match(logical_line) and not RE_USE_JSONUTILS_VALID_LINE.match(logical_line)): yield(0, "S375: Use jsonutils from oslo_serialization instead" " of json")
def use_jsonutils(logical_line, filename): """Check to prevent importing json in sahara code. S375 """ if pep8.noqa(logical_line): return if (RE_USE_JSONUTILS_INVALID_LINE.match(logical_line) and not RE_USE_JSONUTILS_VALID_LINE.match(logical_line)): yield (0, "S375: Use jsonutils from oslo_serialization instead" " of json")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory # and the Xen utilities if ("nova/tests" in filename or "plugins/xenserver/xenapi/etc/xapi.d" in filename): return if pep8.noqa(physical_line): return msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def use_jsonutils(logical_line, filename): """Check to prevent importing json in sahara code. S375 """ if pep8.noqa(logical_line): return invalid_line = re.compile(r"(import\s+json)") valid_line = re.compile(r"(import\s+jsonschema)") if (re.match(invalid_line, logical_line) and not re.match(valid_line, logical_line)): yield(0, "S375: Use jsonutils from oslo_serialization instead" " of json")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory if "octavia/tests" in filename: return if pep8.noqa(physical_line): return msg = "O320: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg) if _directory_to_check_translation(filename): msg = "O320: Log messages require translation hints!" for log_translation_hint in log_translation_hints: if log_translation_hint.match(logical_line): yield (0, msg)
def validate_log_translations(logical_line, physical_line, filename=None): if pep8.noqa(physical_line): return msg = "M328: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "M329: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "M330: LOG.warning, LOG.warn messages require translations `_LW()`!" if log_translation_LW.match(logical_line): yield (0, msg) msg = "M321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def use_jsonutils(logical_line, filename): """Check to prevent importing json in sahara code. S375 """ if pep8.noqa(logical_line): return ignore_dirs = ["sahara/openstack/common"] for dir in ignore_dirs: if dir in filename: return invalid_line = re.compile(r"(import\s+json)") valid_line = re.compile(r"(import\s+jsonschema)") if re.match(invalid_line, logical_line) and not re.match(valid_line, logical_line): yield (0, "S375: Use jsonutils from oslo_serialization instead" " of json")
def hacking_except_format(logical_line, physical_line): r"""Check for 'except:'. OpenStack HACKING guide recommends not using except: Do not write "except:", use "except Exception:" at the very least Okay: try:\n pass\nexcept Exception:\n pass H201: try:\n pass\nexcept:\n pass H201: except: Okay: try:\n pass\nexcept: # noqa\n pass """ if pep8.noqa(physical_line): return if logical_line.startswith("except:"): yield 6, "H201: no 'except:' at least use 'except Exception:'"
def hacking_except_format_assert(logical_line, physical_line): r"""Check for 'assertRaises(Exception'. OpenStack HACKING guide recommends not using assertRaises(Exception...): Do not use overly broad Exception type Okay: self.assertRaises(NovaException, foo) Okay: self.assertRaises(ExceptionStrangeNotation, foo) H202: self.assertRaises(Exception, foo) H202: self.assertRaises(Exception) Okay: self.assertRaises(Exception) # noqa Okay: self.assertRaises(Exception, foo) # noqa """ if pep8.noqa(physical_line): return if re.match(r"self\.assertRaises\(Exception[,\)]", logical_line): yield 1, "H202: assertRaises Exception too broad"
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory if "os_win/tests" in filename: return if pep8.noqa(physical_line): return msg = "N328: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "N329: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "N330: LOG.warning, LOG.warn messages require translations `_LW()`!" if log_translation_LW.match(logical_line): yield (0, msg) msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def validate_log_translations(logical_line, physical_line, filename): """T101 - Log messages require translation hints. :param logical_line: The logical line to check. :param physical_line: The physical line to check. :param filename: The file name where the logical line exists. :returns: None if the logical line passes the check, otherwise a tuple is yielded that contains the offending index in logical line and a message describe the check validation failure. """ if _translation_is_not_expected(filename): return if pep8.noqa(physical_line): return msg = "T101: Untranslated Log message." if _log_translation_hint.match(logical_line): yield (0, msg)
def dont_put_admin_tests_on_nonadmin_path(logical_line, physical_line, filename): """Check admin tests should exist under admin path T115 """ if 'tempest/api/' not in filename: return if pep8.noqa(physical_line): return if not re.match(r'class .*Test.*\(.*Admin.*\):', logical_line): return if not re.match(r'.\/tempest\/api\/.*\/admin\/.*', filename): msg = 'T115: All admin tests should exist under admin path.' yield(0, msg)
def hacking_python3x_print_function(logical_line, physical_line): r"""Check that all occurrences look like print functions, not print operator. As of Python 3.x, the print operator has been removed. Okay: print(msg) Okay: print (msg) Okay: print msg # noqa H233: print msg H233: print >>sys.stderr, "hello" H233: print msg, """ if pep8.noqa(physical_line): return for match in re.finditer(r"\bprint\s+[^\(]", logical_line): yield match.start(0), ( "H233: Python 3.x incompatible use of print operator")
def no_translate_logs(logical_line, physical_line, filename): """T105 - Log messages shouldn't be translated from the Pike release. :param logical_line: The logical line to check. :param physical_line: The physical line to check. :param filename: The file name where the logical line exists. :returns: None if the logical line passes the check, otherwise a tuple is yielded that contains the offending index in logical line and a message describe the check validation failure. """ if _translation_is_not_expected(filename): return if pep8.noqa(physical_line): return msg = "T105: Log message shouldn't be translated." if _translated_log.match(logical_line): yield (0, msg)
def validate_log_translations(logical_line, physical_line, filename): """N531 - Log messages require translation hints. :param logical_line: The logical line to check. :param physical_line: The physical line to check. :param filename: The file name where the logical line exists. :returns: None if the logical line passes the check, otherwise a tuple is yielded that contains the offending index in logical line and a message describe the check validation failure. """ if _translation_is_not_expected(filename): return if pep8.noqa(physical_line): return msg = "N531: Log messages require translation hints!" if _log_translation_hint.match(logical_line): yield (0, msg)
def hacking_no_locals(logical_line, physical_line, tokens): """Do not use locals() for string formatting. Okay: 'locals()' Okay: 'locals' Okay: locals() Okay: print(locals()) H501: print("%(something)" % locals()) Okay: print("%(something)" % locals()) # noqa """ if pep8.noqa(physical_line): return for_formatting = False for token_type, text, start, _, _ in tokens: if text == "%" and token_type == tokenize.OP: for_formatting = True if (for_formatting and token_type == tokenize.NAME and text == "locals" and "locals()" in logical_line): yield (start[1], "H501: Do not use locals() for string formatting")
def dont_put_admin_tests_on_nonadmin_path(logical_line, physical_line, filename): """Check admin tests should exist under admin path T115 """ if 'tempest/api/' not in filename: return if pep8.noqa(physical_line): return if not re.match('class .*Test.*\(.*Admin.*\):', logical_line): return if not re.match('.\/tempest\/api\/.*\/admin\/.*', filename): msg = 'T115: All admin tests should exist under admin path.' yield(0, msg)
def hacking_python3x_metaclass(logical_line, physical_line): r"""Check for metaclass to be Python 3.x compatible. Okay: @six.add_metaclass(Meta)\nclass Foo():\n pass Okay: @six.with_metaclass(Meta)\nclass Foo():\n pass Okay: class Foo():\n '''docstring\n\n __metaclass__ = Meta\n''' H236: class Foo():\n __metaclass__ = Meta H236: class Foo():\n foo=bar\n __metaclass__ = Meta H236: class Foo():\n '''docstr.'''\n __metaclass__ = Meta H236: class Foo():\n __metaclass__ = \\\n Meta Okay: class Foo():\n __metaclass__ = Meta # noqa """ if pep8.noqa(physical_line): return split_line = logical_line.split() if (split_line[0] == '__metaclass__' and split_line[1] == '='): yield (logical_line.find('__metaclass__'), "H236: Python 3.x incompatible __metaclass__, " "use six.add_metaclass()")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory # and the Xen utilities if "nova/tests" in filename or "plugins/xenserver/xenapi/etc/xapi.d" in filename: return if pep8.noqa(physical_line): return msg = "N328: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "N329: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "N330: LOG.warning, LOG.warn messages require translations `_LW()`!" if log_translation_LW.match(logical_line): yield (0, msg) msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def _common_service_clients_check(logical_line, physical_line, filename, ignored_list_file=None): if not re.match("tempest/(lib/)?services/.*", filename): return False if ignored_list_file is not None: ignored_list = [] with open("tempest/hacking/" + ignored_list_file) as f: for line in f: ignored_list.append(line.strip()) if filename in ignored_list: return False if not METHOD.match(physical_line): return False if pep8.noqa(physical_line): return False return True
def check_delayed_string_interpolation(logical_line, physical_line, filename): """Check whether string interpolation is delayed at logging calls Not correct: LOG.debug('Example: %s' % 'bad') Correct: LOG.debug('Example: %s', 'good') N354 """ if "nova/tests" in filename: return if pep8.noqa(physical_line): return if log_string_interpolation.match(logical_line): yield(logical_line.index('%'), "N354: String interpolation should be delayed to be " "handled by the logging code, rather than being done " "at the point of the logging call. " "Use ',' instead of '%'.")
def hacking_python3x_metaclass(logical_line, physical_line): r"""Check for metaclass to be Python 3.x compatible. Okay: @six.add_metaclass(Meta)\nclass Foo():\n pass Okay: @six.with_metaclass(Meta)\nclass Foo():\n pass Okay: class Foo():\n '''docstring\n\n __metaclass__ = Meta\n''' H236: class Foo():\n __metaclass__ = Meta H236: class Foo():\n foo=bar\n __metaclass__ = Meta H236: class Foo():\n '''docstr.'''\n __metaclass__ = Meta H236: class Foo():\n __metaclass__ = \\\n Meta Okay: class Foo():\n __metaclass__ = Meta # noqa """ if pep8.noqa(physical_line): return split_line = logical_line.split() if(len(split_line) > 2 and split_line[0] == '__metaclass__' and split_line[1] == '='): yield (logical_line.find('__metaclass__'), "H236: Python 3.x incompatible __metaclass__, " "use six.add_metaclass()")
def check_delayed_string_interpolation(logical_line, physical_line, filename): """Check whether string interpolation is delayed at logging calls Not correct: LOG.debug('Example: %s' % 'bad') Correct: LOG.debug('Example: %s', 'good') N354 """ if "nova/tests" in filename: return if pep8.noqa(physical_line): return if log_string_interpolation.match(logical_line): yield (logical_line.index('%'), "N354: String interpolation should be delayed to be " "handled by the logging code, rather than being done " "at the point of the logging call. " "Use ',' instead of '%'.")
def check_context_log(logical_line, physical_line, filename): """check whether context is being passed to the logs Not correct: LOG.info(_LI("Rebooting instance"), context=context) Correct: LOG.info(_LI("Rebooting instance")) https://bugs.launchpad.net/nova/+bug/1500896 N353 """ if "nova/tests" in filename: return if pep8.noqa(physical_line): return if log_remove_context.match(logical_line): yield (0, "N353: Nova is using oslo.context's RequestContext " "which means the context object is in scope when " "doing logging using oslo.log, so no need to pass it as" "kwarg.")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory # and the Xen utilities if ("nova/tests" in filename or "plugins/xenserver/xenapi/etc/xapi.d" in filename): return if pep8.noqa(physical_line): return msg = "N328: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "N329: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "N330: LOG.warning, LOG.warn messages require translations `_LW()`!" if log_translation_LW.match(logical_line): yield (0, msg) msg = "N321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def string_quotes_are_valid(node, # type: ast.AST valid_string_quote, # type: str physical_line # type: str ): # type: (...) -> bool """Checks a string or bytes node's string quotes for validity. Args: node: Node to verify quotes of. valid_string_quote: The correct string quote that should be used. physical_line: The physical line that node originated from. Returns: Whether the node's string quotes are valid. """ # Ignore multi-line strings. if node.col_offset == -1: return True if pep8.noqa(physical_line): return True max_prefix_len = len('r"""') prefix = physical_line[node.col_offset:node.col_offset+max_prefix_len] if isinstance(node, ast.Bytes): node.s = node.s.decode('ascii') # Ignore string prefix (e.g. r''). if prefix[0].isalpha(): prefix = prefix[1:] string_quote = prefix[0] if (string_quote != valid_string_quote and opposite_string_quotes[string_quote] not in node.s and not prefix.startswith(StringQuotes.triple_single) and not prefix.startswith(StringQuotes.triple_double)): return False else: return True
def _common_service_clients_check(logical_line, physical_line, filename, ignored_list_file=None): if not re.match('tempest/(lib/)?services/.*', filename): return False if ignored_list_file is not None: ignored_list = [] with open('tempest/hacking/' + ignored_list_file) as f: for line in f: ignored_list.append(line.strip()) if filename in ignored_list: return False if not METHOD.match(physical_line): return False if pep8.noqa(physical_line): return False return True
def check_context_log(logical_line, physical_line, filename): """check whether context is being passed to the logs Not correct: LOG.info(_LI("Rebooting instance"), context=context) Correct: LOG.info(_LI("Rebooting instance")) https://bugs.launchpad.net/nova/+bug/1500896 N353 """ if "nova/tests" in filename: return if pep8.noqa(physical_line): return if log_remove_context.match(logical_line): yield(0, "N353: Nova is using oslo.context's RequestContext " "which means the context object is in scope when " "doing logging using oslo.log, so no need to pass it as" "kwarg.")
def validate_log_translations(logical_line, physical_line, filename): # Translations are not required in the test directory if pep8.noqa(physical_line): return msg = "G322: LOG.info messages require translations `_LI()`!" if log_translation_info.match(logical_line): yield (0, msg) msg = "G323: LOG.exception messages require translations `_LE()`!" if log_translation_exception.match(logical_line): yield (0, msg) msg = "G324: LOG.error messages require translations `_LE()`!" if log_translation_error.match(logical_line): yield (0, msg) msg = "G325: LOG.critical messages require translations `_LC()`!" if log_translation_critical.match(logical_line): yield (0, msg) msg = "G326: LOG.warning messages require translations `_LW()`!" if log_translation_warning.match(logical_line): yield (0, msg) msg = "G321: Log messages require translations!" if log_translation.match(logical_line): yield (0, msg)
def hacking_python3x_except_compatible(logical_line, physical_line): r"""Check for except statements to be Python 3.x compatible As of Python 3.x, the construct 'except x,y:' has been removed. Use 'except x as y:' instead. Okay: try:\n pass\nexcept Exception:\n pass Okay: try:\n pass\nexcept (Exception, AttributeError):\n pass H231: try:\n pass\nexcept AttributeError, e:\n pass Okay: try:\n pass\nexcept AttributeError, e: # noqa\n pass """ if pep8.noqa(physical_line): return def is_old_style_except(logical_line): return (',' in logical_line and ')' not in logical_line.rpartition(',')[2]) if (logical_line.startswith("except ") and logical_line.endswith(':') and is_old_style_except(logical_line)): yield 0, "H231: Python 3.x incompatible 'except x,y:' construct"