commit: c415e94f833acc4b824cf2b6db332244e97313e9 Author: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> AuthorDate: Sat Oct 22 08:21:13 2022 +0000 Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> CommitDate: Fri Oct 28 13:15:03 2022 +0000 URL: https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=c415e94f
ExcessiveLineCheck: check for too long lines Closes: https://bugs.gentoo.org/440686 Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org> src/pkgcheck/checks/codingstyle.py | 39 +++++++++++++++++++++++++ tests/checks/test_codingstyle.py | 59 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/pkgcheck/checks/codingstyle.py b/src/pkgcheck/checks/codingstyle.py index e8a2f45a..0933c492 100644 --- a/src/pkgcheck/checks/codingstyle.py +++ b/src/pkgcheck/checks/codingstyle.py @@ -1079,3 +1079,42 @@ class EclassUnquotedVariablesCheck(_UnquotedVariablesCheck): def feed(self, eclass): for var_name, lines in self._feed(eclass): yield EclassUnquotedVariable(var_name, lines=lines, eclass=eclass.name) + + +class ExcessiveLineLength(results.LinesResult, results.Style): + """Line is longer than 120 characters.""" + + line_length = 120 + word_length = 110 + + @property + def desc(self): + return f'excessive line length (over {self.line_length} characters) {self.lines_str}' + + +class LineLengthCheck(Check): + """Scan ebuild for lines with excessive length.""" + + _source = sources.EbuildFileRepoSource + known_results = frozenset([ExcessiveLineLength]) + + def __init__(self, options, **kwargs): + super().__init__(options, **kwargs) + self.exception = re.compile(r'\s*(?:DESCRIPTION|KEYWORDS|IUSE)=') + str_length = f'[^\'\"]{{{ExcessiveLineLength.word_length},}}' + self.long_string = re.compile(rf'"{str_length}"|\'{str_length}\'') + + def feed(self, pkg): + lines = [] + for lineno, line in enumerate(pkg.lines, 1): + if len(line) <= ExcessiveLineLength.line_length: + continue + if self.exception.match(line): + continue # exception variables which are fine to be long + if max(map(len, line.split())) > ExcessiveLineLength.word_length: + continue # if one part of the line is very long word + if self.long_string.search(line): + continue # skip lines with long quoted string + lines.append(lineno) + if lines: + yield ExcessiveLineLength(lines=lines, pkg=pkg) diff --git a/tests/checks/test_codingstyle.py b/tests/checks/test_codingstyle.py index 3becc919..1c6a0075 100644 --- a/tests/checks/test_codingstyle.py +++ b/tests/checks/test_codingstyle.py @@ -433,3 +433,62 @@ class TestStaticSrcUri(misc.ReportTestCase): r = self.assertReport(self.check, self._prepare_pkg('Diffball-0.1.2.3', pkgver='Diffball-0.1.2.3')) assert r.static_str == 'Diffball-0.1.2.3' assert r.replacement == '${P}' + + +class TestExcessiveLineLength(misc.ReportTestCase): + + check_kls = codingstyle.LineLengthCheck + check = check_kls(None) + word_length = codingstyle.ExcessiveLineLength.word_length + + + @staticmethod + def _prepare_pkg(*lines: str): + fake_pkg = misc.FakePkg("dev-util/diffball-0", ebuild=''.join(lines), lines=lines) + data = ''.join(lines).encode() + return _ParsedPkg(data, pkg=fake_pkg) + + def test_normal_length(self): + self.assertNoReport(self.check, self._prepare_pkg('echo "short line"')) + + def test_long_line(self): + r = self.assertReport(self.check, self._prepare_pkg(f'echo {"a " * codingstyle.ExcessiveLineLength.line_length}')) + assert r.lines == (1, ) + + def test_multiple_lines(self): + r = self.assertReport(self.check, self._prepare_pkg( + f'echo {"a " * codingstyle.ExcessiveLineLength.line_length}', + 'echo "short line"', + f'echo {"Hello " * codingstyle.ExcessiveLineLength.line_length}', + )) + assert r.lines == (1, 3) + + @pytest.mark.parametrize('variable', ('DESCRIPTION', 'KEYWORDS', 'IUSE')) + def test_special_variables(self, variable): + self.assertNoReport(self.check, self._prepare_pkg( + f'{variable}="{"a " * codingstyle.ExcessiveLineLength.line_length}"', + f' {variable}="{"a " * codingstyle.ExcessiveLineLength.line_length}"', + f'\t\t{variable}="{"a " * codingstyle.ExcessiveLineLength.line_length}"', + )) + + def test_long_words(self): + long_word = 'a' * self.word_length + 'b' + medium_word = 'a' * (self.word_length // 2) + r = self.assertReport(self.check, self._prepare_pkg( + f'echo {"a" * codingstyle.ExcessiveLineLength.line_length}', + f'echo {medium_word} {long_word}', + f'echo {medium_word} {long_word[:-5]}', + )) + assert r.lines == (3, ) + + def test_long_quotes(self): + # The exception is for any quoted string with length >= word_length. + # Each quoted string is computed by itself. + long_word = 'a ' * (self.word_length // 2) + 'b' # long quoted string, skipped + medium_word = 'a ' * (self.word_length // 4) # not long enough string, not skipped + r = self.assertReport(self.check, self._prepare_pkg( + f'echo "{"a" * codingstyle.ExcessiveLineLength.line_length}"', + f'echo "{medium_word}" "{long_word}"', + 'echo' + f' "{medium_word}"' * 3, + )) + assert r.lines == (3, )