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, )

Reply via email to