diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6919581..9dc30df 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - tomli - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.9 + rev: v0.9.10 hooks: - id: ruff diff --git a/cpplint.py b/cpplint.py index 0e658d2..b2196da 100755 --- a/cpplint.py +++ b/cpplint.py @@ -1407,9 +1407,9 @@ def AddFilters(self, filters): if clean_filt: self.filters.append(clean_filt) for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - f' ({filt} does not)') + if not filt.startswith(('+', '-')): + msg = f'Every filter in --filters must start with + or - ({filt} does not)' + raise ValueError(msg) def BackupFilters(self): """ Saves the current filter list to backup storage.""" @@ -1618,7 +1618,7 @@ def Check(self, error, filename, linenum): trigger = base_trigger * 2**_VerboseLevel() if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + error_level = int(math.log2(self.lines_in_function / base_trigger)) # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... if error_level > 5: error_level = 5 @@ -1769,7 +1769,9 @@ def _ShouldPrintError(category, confidence, filename, linenum): if category_match and file_match and line_match: is_filtered = False else: - assert False # should have been checked for in SetFilter. + # should have been checked for in SetFilter. + msg = f'Invalid filter: {one_filter}' + raise ValueError(msg) if is_filtered: return False @@ -2357,7 +2359,8 @@ def CheckForCopyright(filename, lines, error): # We'll say it should occur by line 10. Don't forget there's a # placeholder line at the front. for line in range(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break + if re.search(r'Copyright', lines[line], re.IGNORECASE): + break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, 'No copyright message found. ' @@ -2376,8 +2379,7 @@ def GetIndentLevel(line): indent = re.match(r'^( *)\S', line) if indent: return len(indent.group(1)) - else: - return 0 + return 0 def PathSplitToList(path): """Returns the path split into a list by the separator. @@ -3108,14 +3110,16 @@ def InTemplateArgumentList(self, clean_lines, linenum, pos): # These things do not look like template argument list: # class Suspect { # class Suspect x; } - if token in ('{', '}', ';'): return False + if token in ('{', '}', ';'): + return False # These things look like template argument list: # template # template # template # template - if token in ('>', '=', '[', ']', '.'): return True + if token in ('>', '=', '[', ']', '.'): + return True # Check if token is an unmatched '<'. # If not, move on to the next character. @@ -4912,8 +4916,7 @@ def GetLineWidth(line): width += 1 return width - else: - return len(line) + return len(line) def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, @@ -4985,9 +4988,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, # Check if the line is a header guard. is_header_guard = False if IsHeaderExtension(file_extension): - if (line.startswith(f'#ifndef {cppvar}') or - line.startswith(f'#define {cppvar}') or - line.startswith(f'#endif // {cppvar}')): + if line.startswith((f'#ifndef {cppvar}', f'#define {cppvar}', f'#endif // {cppvar}')): is_header_guard = True # #include lines and header guards can be long, since there's no clean way to # split them. @@ -5126,8 +5127,7 @@ def _ClassifyInclude(fileinfo, include, used_angle_brackets, include_order="defa return _CPP_SYS_HEADER if is_std_c_header: return _C_SYS_HEADER - else: - return _OTHER_SYS_HEADER + return _OTHER_SYS_HEADER # If the target file and the include we're checking share a # basename when we drop common extensions, and the include @@ -5277,7 +5277,7 @@ def _GetTextInside(text, start_pattern): closing_punctuation = set(dict.values(matching_punctuation)) # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) + match = re.search(start_pattern, text, re.MULTILINE) if not match: # start_pattern not found in text. return None start_position = match.end(0) @@ -5422,7 +5422,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, match = re.match(r'([\w.\->()]+)$', printf_args) if match and match.group(1) != '__VA_ARGS__': function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) + line, re.IGNORECASE).group(1) error(filename, linenum, 'runtime/printf', 4, 'Potential format string bug. Do' f' {function_name}("%s", {match.group(1)}) instead.') @@ -5458,17 +5458,25 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, skip_next = False continue - if re.search(r'sizeof\(.+\)', tok): continue - if re.search(r'arraysize\(\w+\)', tok): continue + if re.search(r'sizeof\(.+\)', tok): + continue + if re.search(r'arraysize\(\w+\)', tok): + continue tok = tok.lstrip('(') tok = tok.rstrip(')') - if not tok: continue - if re.match(r'\d+', tok): continue - if re.match(r'0[xX][0-9a-fA-F]+', tok): continue - if re.match(r'k[A-Z0-9]\w*', tok): continue - if re.match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if re.match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + if not tok: + continue + if re.match(r'\d+', tok): + continue + if re.match(r'0[xX][0-9a-fA-F]+', tok): + continue + if re.match(r'k[A-Z0-9]\w*', tok): + continue + if re.match(r'(.+::)?k[A-Z0-9]\w*', tok): + continue + if re.match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): + continue # A catch all for tricky sizeof cases, including 'sizeof expression', # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' # requires skipping the next token because we split on ' ' and '*'. @@ -5783,7 +5791,7 @@ def CheckForNonConstReference(filename, clean_lines, linenum, r')\s*\(') if re.search(allowed_functions, line): return - elif not re.search(r'\S+\([^)]*$', line): + if not re.search(r'\S+\([^)]*$', line): # Don't see an allowed function on this line. Actually we # didn't see any function name on this line, so this is likely a # multi-line parameter list. Try a bit harder to catch this case. @@ -5955,8 +5963,7 @@ def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): return False # operator++(int) and operator--(int) - if (context.endswith(' operator++') or context.endswith(' operator--') or - context.endswith('::operator++') or context.endswith('::operator--')): + if context.endswith((' operator++', ' operator--', '::operator++', '::operator--')): return False # A single unnamed argument for a function tends to look like old style cast. @@ -6273,7 +6280,8 @@ def CheckRedundantVirtual(filename, clean_lines, linenum, error): # Look for "virtual" on current line. line = clean_lines.elided[linenum] virtual = re.match(r'^(.*)(\bvirtual\b)(.*)$', line) - if not virtual: return + if not virtual: + return # Ignore "virtual" keywords that are near access-specifiers. These # are only used in class base-specifier and do not apply to member @@ -6285,7 +6293,8 @@ def CheckRedundantVirtual(filename, clean_lines, linenum, error): # Ignore the "virtual" keyword from virtual base classes. Usually # there is a column on the same line in these cases (virtual base # classes are rare in google3 because multiple inheritance is rare). - if re.match(r'^.*[^:]:[^:].*$', line): return + if re.match(r'^.*[^:]:[^:].*$', line): + return # Look for the next opening parenthesis. This is the start of the # parameter list (possibly on the next line shortly after virtual). @@ -6374,7 +6383,7 @@ def IsBlockInNameSpace(nesting_state, is_forward_declaration): if len(nesting_state.stack) >= 1: if isinstance(nesting_state.stack[-1], _NamespaceInfo): return True - elif (len(nesting_state.stack) > 1 and + if (len(nesting_state.stack) > 1 and isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and isinstance(nesting_state.stack[-2], _NamespaceInfo)): return True @@ -6453,7 +6462,8 @@ def ProcessLine(filename, file_extension, clean_lines, line, nesting_state.Update(filename, clean_lines, line, error) CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, error) - if nesting_state.InAsmBlock(): return + if nesting_state.InAsmBlock(): + return CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error, cppvar) @@ -6895,9 +6905,8 @@ def _ParseFilterSelector(parameter): second_colon_pos = parameter.find(":", colon_pos + 1) if second_colon_pos == -1: return category, parameter[colon_pos + 1:], -1 - else: - return category, parameter[colon_pos + 1: second_colon_pos], \ - int(parameter[second_colon_pos + 1:]) + return category, parameter[colon_pos + 1: second_colon_pos], \ + int(parameter[second_colon_pos + 1:]) def _ExpandDirectories(filenames): """Searches a list of filenames and replaces directories in the list with diff --git a/cpplint_clitest.py b/cpplint_clitest.py index b23449a..d49d08c 100755 --- a/cpplint_clitest.py +++ b/cpplint_clitest.py @@ -33,11 +33,12 @@ import glob import os -import sys -import subprocess -import unittest import shutil +import subprocess +import sys import tempfile +import unittest + from pytest import mark from testfixtures import compare @@ -101,7 +102,7 @@ def setUpClass(cls): except Exception: try: cls.tearDownClass() - except Exception: + except Exception: # noqa: BLE001 pass raise diff --git a/cpplint_unittest.py b/cpplint_unittest.py index c0a4010..b6c6a2a 100755 --- a/cpplint_unittest.py +++ b/cpplint_unittest.py @@ -43,16 +43,17 @@ import sys import tempfile import unittest -from parameterized import parameterized + import pytest +from parameterized import parameterized import cpplint + def codecs_latin_encode(x): if sys.version_info < (3,): return x - else: - return codecs.latin_1_encode(x)[0] + return codecs.latin_1_encode(x)[0] # This class works as an error collector and replaces cpplint.Error # function for the unit tests. We also verify each category we see @@ -80,8 +81,7 @@ def __call__(self, filename, linenum, def Results(self): if len(self._errors) < 2: return ''.join(self._errors) # Most tests expect to have a string. - else: - return self._errors # Let's give a list if there is more than one. + return self._errors # Let's give a list if there is more than one. def ResultList(self): return self._errors @@ -115,7 +115,7 @@ class EnterableList(list): def __enter__(self): return self - def __exit__(self, type, value, tb): + def __exit__(self, type, value, tb): # noqa: A002 return self self.mock_file = EnterableList(mock_file) @@ -4283,8 +4283,8 @@ def testParseArguments(self): self.assertEqual(set(['hpp', 'h', 'cpp']), cpplint.GetAllExtensions()) finally: - sys.stdout == sys.__stdout__ - sys.stderr == sys.__stderr__ + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ cpplint._cpplint_state.output_format = old_output_format cpplint._cpplint_state.verbose_level = old_verbose_level cpplint._cpplint_state.filters = old_filters @@ -4657,6 +4657,7 @@ def GetBuildHeaderGuardPreprocessorSymbol(self, file_path): error) if matched is not None: return matched.group(1) + return None def testBuildHeaderGuard(self): file_path = 'mydir/foo.h' @@ -5276,7 +5277,7 @@ def testBuildStorageClass(self): # Make sure that the declaration is logged if there's an error. # Seed generator with an integer for absolute reproducibility. random.seed(25) - for unused_i in range(10): + for _i in range(10): # Build up random list of non-storage-class declaration specs. other_decl_specs = [random.choice(qualifiers), random.choice(signs), random.choice(types)] @@ -5660,7 +5661,7 @@ def testRegression(self): def Format(includes): include_list = [] for item in includes: - if item.startswith('"') or item.startswith('<'): + if item.startswith(('"', '<')): include_list.append('#include %s\n' % item) else: include_list.append(item + '\n') diff --git a/pyproject.toml b/pyproject.toml index 5bc3130..c1e3f74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,83 @@ py-modules = [ "cpplint" ] include-package-data = false [tool.ruff] -lint.ignore = [ "E701" ] +target-version = "py38" + +line-length = 116 +lint.select = [ + "A", # flake8-builtins + "AIR", # Airflow + "ASYNC", # flake8-async + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C90", # McCabe cyclomatic complexity + "DJ", # flake8-django + "DTZ", # flake8-datetimez + "E", # pycodestyle + "EM", # flake8-errmsg + "EXE", # flake8-executable + "F", # Pyflakes + "FA", # flake8-future-annotations + "FAST", # FastAPI + "FBT", # flake8-boolean-trap + "FIX", # flake8-fixme + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "INT", # flake8-gettext + "ISC", # flake8-implicit-str-concat + "LOG", # flake8-logging + "NPY", # NumPy-specific rules + "PD", # pandas-vet + "PERF", # Perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PLC", # Pylint conventions + "PLE", # Pylint errors + "PYI", # flake8-pyi + "RET", # flake8-return + "RSE", # flake8-raise + "SLOT", # flake8-slots + "T10", # flake8-debugger + "TC", # flake8-type-checking + "TID", # flake8-tidy-imports + "TRY", # tryceratops + "W", # pycodestyle + "YTT", # flake8-2020 + # "ANN", # flake8-annotations + # "ARG", # flake8-unused-arguments + # "C4", # flake8-comprehensions + # "COM", # flake8-commas + # "CPY", # flake8-copyright + # "D", # pydocstyle + # "DOC", # pydoclint + # "ERA", # eradicate + # "N", # pep8-naming + # "PLR", # Pylint refactor + # "PLW", # Pylint warnings + # "PT", # flake8-pytest-style + # "PTH", # flake8-use-pathlib + # "Q", # flake8-quotes + # "RUF", # Ruff-specific rules + # "S", # flake8-bandit + # "SIM", # flake8-simplify + # "SLF", # flake8-self + # "T20", # flake8-print + # "TD", # flake8-todos + # "UP", # pyupgrade +] +lint.ignore = [ + "FBT003", # flake8-boolean-trap + "FIX002", # flake8-fixme + "ISC003", # flake8-implicit-str-concat + "PIE790", # Unnecessary `pass` statement +] +lint.per-file-ignores."cpplint.py" = [ "ICN001", "PERF401" ] +lint.per-file-ignores."cpplint_unittest.py" = [ "FLY002" ] +lint.mccabe.max-complexity = 29 [tool.pytest.ini_options] python_files = [ "*test.py" ]