From patchwork Fri Oct 18 19:32:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 21697 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3E856C32AF for ; Fri, 18 Oct 2024 19:32:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BFF3465391; Fri, 18 Oct 2024 21:32:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="amTEmReL"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9208C633C7 for ; Fri, 18 Oct 2024 21:32:53 +0200 (CEST) Received: from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi [81.175.209.231]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 94B5B21C; Fri, 18 Oct 2024 21:31:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1729279869; bh=KgNxz85NUESoziGNOveIgBNP5odlnilj6YbBkXm3D0Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=amTEmReLkpYuOke3kgPCz4PI72SUVcfGoxV1hj5GjRS02xyKmMpZdOgQMqMWh8D5R xZm8P0rrJRF/74QuTXADp71FgfVWWrP4nYH4uwJmxxmh2Vl1xlb990qdRTDD6Owe1L 3+r+ud6s2q50ETjzURSEs28pMEd5Moh7sazz3Uzg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 1/4] utils: checkstyle.py: Factor out common code to new CheckerBase class Date: Fri, 18 Oct 2024 22:32:43 +0300 Message-ID: <20241018193246.805-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> References: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The CommitChecker, StyleChecker and Formatter classes duplicate code. Create a new CheckerBase class to factor out common code. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- utils/checkstyle.py | 140 ++++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 89 deletions(-) diff --git a/utils/checkstyle.py b/utils/checkstyle.py index ab89c0a14fab..1de518953dcd 100755 --- a/utils/checkstyle.py +++ b/utils/checkstyle.py @@ -334,37 +334,57 @@ class Amendment(Commit): class ClassRegistry(type): def __new__(cls, clsname, bases, attrs): newclass = super().__new__(cls, clsname, bases, attrs) - if bases: - bases[0].subclasses.append(newclass) - bases[0].subclasses.sort(key=lambda x: getattr(x, 'priority', 0), - reverse=True) + if bases and bases[0] != CheckerBase: + base = bases[0] + + if not hasattr(base, 'subclasses'): + base.subclasses = [] + base.subclasses.append(newclass) + base.subclasses.sort(key=lambda x: getattr(x, 'priority', 0), + reverse=True) return newclass +class CheckerBase(metaclass=ClassRegistry): + # + # Class methods + # + @classmethod + def instances(cls, obj, names): + for instance in cls.subclasses: + if names and instance.__name__ not in names: + continue + if instance.supports(obj): + yield instance + + @classmethod + def supports(cls, obj): + if hasattr(cls, 'commit_types'): + return type(obj) in cls.commit_types + + if hasattr(cls, 'patterns'): + for pattern in cls.patterns: + if fnmatch.fnmatch(os.path.basename(obj), pattern): + return True + + return False + + @classmethod + def all_patterns(cls): + patterns = set() + for instance in cls.subclasses: + if hasattr(instance, 'patterns'): + patterns.update(instance.patterns) + + return patterns + + # ------------------------------------------------------------------------------ # Commit Checkers # -class CommitChecker(metaclass=ClassRegistry): - subclasses = [] - - def __init__(self): - pass - - # - # Class methods - # - @classmethod - def checkers(cls, commit, names): - for checker in cls.subclasses: - if names and checker.__name__ not in names: - continue - if checker.supports(commit): - yield checker - - @classmethod - def supports(cls, commit): - return type(commit) in cls.commit_types +class CommitChecker(CheckerBase): + pass class CommitIssue(object): @@ -561,37 +581,8 @@ class TrailersChecker(CommitChecker): # Style Checkers # -class StyleChecker(metaclass=ClassRegistry): - subclasses = [] - - def __init__(self): - pass - - # - # Class methods - # - @classmethod - def checkers(cls, filename, names): - for checker in cls.subclasses: - if names and checker.__name__ not in names: - continue - if checker.supports(filename): - yield checker - - @classmethod - def supports(cls, filename): - for pattern in cls.patterns: - if fnmatch.fnmatch(os.path.basename(filename), pattern): - return True - return False - - @classmethod - def all_patterns(cls): - patterns = set() - for checker in cls.subclasses: - patterns.update(checker.patterns) - - return patterns +class StyleChecker(CheckerBase): + pass class StyleIssue(object): @@ -748,37 +739,8 @@ class ShellChecker(StyleChecker): # Formatters # -class Formatter(metaclass=ClassRegistry): - subclasses = [] - - def __init__(self): - pass - - # - # Class methods - # - @classmethod - def formatters(cls, filename, names): - for formatter in cls.subclasses: - if names and formatter.__name__ not in names: - continue - if formatter.supports(filename): - yield formatter - - @classmethod - def supports(cls, filename): - for pattern in cls.patterns: - if fnmatch.fnmatch(os.path.basename(filename), pattern): - return True - return False - - @classmethod - def all_patterns(cls): - patterns = set() - for formatter in cls.subclasses: - patterns.update(formatter.patterns) - - return patterns +class Formatter(CheckerBase): + pass class CLangFormatter(Formatter): @@ -957,7 +919,7 @@ def check_file(top_level, commit, filename, checkers): after = commit.get_file(filename) formatted = after - for formatter in Formatter.formatters(filename, checkers): + for formatter in Formatter.instances(filename, checkers): formatted = formatter.format(filename, formatted) after = after.splitlines(True) @@ -971,7 +933,7 @@ def check_file(top_level, commit, filename, checkers): # Check for code issues not related to formatting. issues = [] - for checker in StyleChecker.checkers(filename, checkers): + for checker in StyleChecker.instances(filename, checkers): checker = checker(after) for hunk in commit_diff: issues += checker.check(hunk.side('to').touched) @@ -1019,7 +981,7 @@ def check_style(top_level, commit, checkers): issues = 0 # Apply the commit checkers first. - for checker in CommitChecker.checkers(commit, checkers): + for checker in CommitChecker.instances(commit, checkers): for issue in checker.check(commit, top_level): print('%s%s%s' % (Colours.fg(Colours.Yellow), issue.msg, Colours.reset())) issues += 1 From patchwork Fri Oct 18 19:32:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 21698 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 99033C32AF for ; Fri, 18 Oct 2024 19:33:02 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1E0DB65394; Fri, 18 Oct 2024 21:33:02 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="E3geKud4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6ADBE633C6 for ; Fri, 18 Oct 2024 21:32:55 +0200 (CEST) Received: from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi [81.175.209.231]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id EFF6C21C; Fri, 18 Oct 2024 21:31:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1729279871; bh=sRWu18G/T97g8sTHlk8+S//JIV1jOROF4ut9yODdjGY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=E3geKud4WM6LQ4Irkx5/39I0HDjMQ+DT6veRQpgCuyJs396bCrNVmNLMkF79i3nNs LuF/0XnXaoLw2bE0iZoI59J1STg34bt/ldmZcIR5Rf933QlvU9wlNhopJcZsGP5HfH uLSZBPWOZDm6nfLDMfWQlLeEuHkuCws5keOYKvTU= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 2/4] utils: checkstyle.py: Turn check() into a class method for all checkers Date: Fri, 18 Oct 2024 22:32:44 +0300 Message-ID: <20241018193246.805-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> References: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The check() method of StyleChecker subclasses are instance methods, while CommitChecker subclasses use class methods. This makes unified handling of checkers more complicated. Turn the StyleChecker check() method into a class method, passing it the contents to be checked directly. While at it, fix two style issues reported by checkstyle.py. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- utils/checkstyle.py | 48 +++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/utils/checkstyle.py b/utils/checkstyle.py index 1de518953dcd..bc0ddfad8743 100755 --- a/utils/checkstyle.py +++ b/utils/checkstyle.py @@ -598,15 +598,12 @@ class HexValueChecker(StyleChecker): regex = re.compile(r'\b0[xX][0-9a-fA-F]+\b') - def __init__(self, content): - super().__init__() - self.__content = content - - def check(self, line_numbers): + @classmethod + def check(cls, content, line_numbers): issues = [] for line_number in line_numbers: - line = self.__content[line_number - 1] + line = content[line_number - 1] match = HexValueChecker.regex.search(line) if not match: continue @@ -630,15 +627,12 @@ class IncludeChecker(StyleChecker): 'cwchar', 'cwctype', 'math.h') include_regex = re.compile(r'^#include <([a-z.]*)>') - def __init__(self, content): - super().__init__() - self.__content = content - - def check(self, line_numbers): + @classmethod + def check(self, content, line_numbers): issues = [] for line_number in line_numbers: - line = self.__content[line_number - 1] + line = content[line_number - 1] match = IncludeChecker.include_regex.match(line) if not match: continue @@ -664,14 +658,11 @@ class LogCategoryChecker(StyleChecker): log_regex = re.compile(r'\bLOG\((Debug|Info|Warning|Error|Fatal)\)') patterns = ('*.cpp',) - def __init__(self, content): - super().__init__() - self.__content = content - - def check(self, line_numbers): + @classmethod + def check(cls, content, line_numbers): issues = [] for line_number in line_numbers: - line = self.__content[line_number-1] + line = content[line_number - 1] match = LogCategoryChecker.log_regex.search(line) if not match: continue @@ -685,14 +676,11 @@ class LogCategoryChecker(StyleChecker): class MesonChecker(StyleChecker): patterns = ('meson.build',) - def __init__(self, content): - super().__init__() - self.__content = content - - def check(self, line_numbers): + @classmethod + def check(cls, content, line_numbers): issues = [] for line_number in line_numbers: - line = self.__content[line_number-1] + line = content[line_number - 1] pos = line.find('\t') if pos != -1: issues.append(StyleIssue(line_number, [pos, pos], line, @@ -704,13 +692,10 @@ class ShellChecker(StyleChecker): patterns = ('*.sh',) results_line_regex = re.compile(r'In - line ([0-9]+):') - def __init__(self, content): - super().__init__() - self.__content = content - - def check(self, line_numbers): + @classmethod + def check(cls, content, line_numbers): issues = [] - data = ''.join(self.__content).encode('utf-8') + data = ''.join(content).encode('utf-8') try: ret = subprocess.run(['shellcheck', '-Cnever', '-'], @@ -934,9 +919,8 @@ def check_file(top_level, commit, filename, checkers): # Check for code issues not related to formatting. issues = [] for checker in StyleChecker.instances(filename, checkers): - checker = checker(after) for hunk in commit_diff: - issues += checker.check(hunk.side('to').touched) + issues += checker.check(after, hunk.side('to').touched) # Print the detected issues. if len(issues) == 0 and len(formatted_diff) == 0: From patchwork Fri Oct 18 19:32:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 21699 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 5D3BDC32AF for ; Fri, 18 Oct 2024 19:33:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id A90BC65391; Fri, 18 Oct 2024 21:33:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hPO9l+ew"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8D4706538A for ; Fri, 18 Oct 2024 21:32:56 +0200 (CEST) Received: from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi [81.175.209.231]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8715521C; Fri, 18 Oct 2024 21:31:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1729279872; bh=HibV6QN6HFJ32OlNJ2YQbp2Pu7qHJi1MPs3Y6vOalj8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hPO9l+ewnG25bDCbYeCDueB34D1b9Q068fWHQcnf7REPAS98s50nsF4YW9v3a/GrL sMm5Vff9JE4YEX6a/NpMb21AAwXLyVoJpPDcxqbU6PJHqtETFBkvxM/AIj1uCVxr9O oLbO/FxVOkRBwyciDcbXy4J0MTreIQe9EBNhLq0k= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 3/4] utils: checkstyle.py: Print issues using __str__ Date: Fri, 18 Oct 2024 22:32:45 +0300 Message-ID: <20241018193246.805-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> References: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" CommitIssue and StyleIssue classes have different string representations, which are handled by type-specific print() calls in the code that handles the issues. This requires the user to know which type of issue it is dealing with. Simplify that by moving the string representation logic to a __str__() method. Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- utils/checkstyle.py | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/utils/checkstyle.py b/utils/checkstyle.py index bc0ddfad8743..3f841a54d54a 100755 --- a/utils/checkstyle.py +++ b/utils/checkstyle.py @@ -391,6 +391,9 @@ class CommitIssue(object): def __init__(self, msg): self.msg = msg + def __str__(self): + return f'{Colours.fg(Colours.Yellow)}{self.msg}{Colours.reset()}' + class HeaderAddChecker(CommitChecker): commit_types = (Commit, StagedChanges, Amendment) @@ -592,6 +595,24 @@ class StyleIssue(object): self.line = line self.msg = msg + def __str__(self): + s = [] + s.append(f'{Colours.fg(Colours.Yellow)}#{self.line_number}: {self.msg}{Colours.reset()}') + if self.line is not None: + s.append(f'{Colours.fg(Colours.Yellow)}+{self.line.rstrip()}{Colours.reset()}') + + if self.position is not None: + # Align the position marker by using the original line with + # all characters except for tabs replaced with spaces. This + # ensures proper alignment regardless of how the code is + # indented. + start = self.position[0] + prefix = ''.join([c if c == '\t' else ' ' for c in self.line[:start]]) + length = self.position[1] - start - 1 + s.append(f' {prefix}^{"~" * length}') + + return '\n'.join(s) + class HexValueChecker(StyleChecker): patterns = ('*.c', '*.cpp', '*.h') @@ -936,21 +957,7 @@ def check_file(top_level, commit, filename, checkers): if len(issues): issues = sorted(issues, key=lambda i: i.line_number) for issue in issues: - print('%s#%u: %s%s' % (Colours.fg(Colours.Yellow), issue.line_number, - issue.msg, Colours.reset())) - if issue.line is not None: - print('%s+%s%s' % (Colours.fg(Colours.Yellow), issue.line.rstrip(), - Colours.reset())) - - if issue.position is not None: - # Align the position marker by using the original line with - # all characters except for tabs replaced with spaces. This - # ensures proper alignment regardless of how the code is - # indented. - start = issue.position[0] - prefix = ''.join([c if c == '\t' else ' ' for c in issue.line[:start]]) - length = issue.position[1] - start - 1 - print(' ' + prefix + '^' + '~' * length) + print(issue) return len(formatted_diff) + len(issues) @@ -967,7 +974,7 @@ def check_style(top_level, commit, checkers): # Apply the commit checkers first. for checker in CommitChecker.instances(commit, checkers): for issue in checker.check(commit, top_level): - print('%s%s%s' % (Colours.fg(Colours.Yellow), issue.msg, Colours.reset())) + print(issue) issues += 1 # Filter out files we have no checker for. From patchwork Fri Oct 18 19:32:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 21700 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 5EB9BC3300 for ; Fri, 18 Oct 2024 19:33:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E9B8C65396; Fri, 18 Oct 2024 21:33:04 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RDWyhN7F"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D71646538B for ; Fri, 18 Oct 2024 21:32:57 +0200 (CEST) Received: from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi [81.175.209.231]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DE56C21C; Fri, 18 Oct 2024 21:31:13 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1729279874; bh=W6dqA0GHMyI4YC4hIoVftEIx0pRv9/FVT+RZFyaxKAM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RDWyhN7F0uim1i/XHeLMFyB0XrfIJzoe3CUEeTe2Hb2KWyEaoJAoaDl8VYRwbmGcH PKP8EKjlYzebBSAhk6dAMmW5q1TdVZFZNftYEzsP56/CkgDqKE6oldRmUagt+JTUUK v4X/rccuCI1E2M+Va45PKb7uj5gAapCDxKuhSRgg= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Cc: Stefan Klug Subject: [PATCH 4/4] utils: checkstyle.py: Centralize dependency handling for checkers Date: Fri, 18 Oct 2024 22:32:46 +0300 Message-ID: <20241018193246.805-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> References: <20241018193246.805-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The checkstyle.py script depends on external tools. Those dependencies are handled in different ways in different parts of the code. Centralize the management of checker-specific dependencies to simplify the checkers and output more consistent error messages. This fixes a crash in the Pep8Formatter class when the autopep8 dependency is not found. Fixes: 8ffaf376bb53 ("utils: checkstyle: Add a python formatter") Signed-off-by: Laurent Pinchart Reviewed-by: Kieran Bingham --- utils/checkstyle.py | 81 ++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/utils/checkstyle.py b/utils/checkstyle.py index 3f841a54d54a..f6229bbd86ce 100755 --- a/utils/checkstyle.py +++ b/utils/checkstyle.py @@ -23,7 +23,6 @@ import subprocess import sys dependencies = { - 'clang-format': True, 'git': True, } @@ -346,9 +345,6 @@ class ClassRegistry(type): class CheckerBase(metaclass=ClassRegistry): - # - # Class methods - # @classmethod def instances(cls, obj, names): for instance in cls.subclasses: @@ -378,6 +374,22 @@ class CheckerBase(metaclass=ClassRegistry): return patterns + @classmethod + def check_dependencies(cls): + if not hasattr(cls, 'dependencies'): + return [] + + issues = [] + + for command in cls.dependencies: + if command not in dependencies: + dependencies[command] = shutil.which(command) + + if not dependencies[command]: + issues.append(CommitIssue(f'Missing {command} to run {cls.__name__}')) + + return issues + # ------------------------------------------------------------------------------ # Commit Checkers @@ -710,6 +722,7 @@ class MesonChecker(StyleChecker): class ShellChecker(StyleChecker): + dependencies = ('shellcheck',) patterns = ('*.sh',) results_line_regex = re.compile(r'In - line ([0-9]+):') @@ -718,12 +731,8 @@ class ShellChecker(StyleChecker): issues = [] data = ''.join(content).encode('utf-8') - try: - ret = subprocess.run(['shellcheck', '-Cnever', '-'], - input=data, stdout=subprocess.PIPE) - except FileNotFoundError: - issues.append(StyleIssue(0, None, None, 'Please install shellcheck to validate shell script additions')) - return issues + ret = subprocess.run(['shellcheck', '-Cnever', '-'], + input=data, stdout=subprocess.PIPE) results = ret.stdout.decode('utf-8').splitlines() for nr, item in enumerate(results): @@ -750,6 +759,7 @@ class Formatter(CheckerBase): class CLangFormatter(Formatter): + dependencies = ('clang-format',) patterns = ('*.c', '*.cpp', '*.h') priority = -1 @@ -879,17 +889,13 @@ class IncludeOrderFormatter(Formatter): class Pep8Formatter(Formatter): + dependencies = ('autopep8',) patterns = ('*.py',) @classmethod def format(cls, filename, data): - try: - ret = subprocess.run(['autopep8', '--ignore=E501', '-'], - input=data.encode('utf-8'), stdout=subprocess.PIPE) - except FileNotFoundError: - issues.append(StyleIssue(0, None, None, 'Please install autopep8 to format python additions')) - return issues - + ret = subprocess.run(['autopep8', '--ignore=E501', '-'], + input=data.encode('utf-8'), stdout=subprocess.PIPE) return ret.stdout.decode('utf-8') @@ -908,6 +914,24 @@ class StripTrailingSpaceFormatter(Formatter): # Style checking # +def check_commit(top_level, commit, checkers): + issues = [] + + # Apply the commit checkers first. + for checker in CommitChecker.instances(commit, checkers): + issues_ = checker.check_dependencies() + if issues_: + issues += issues_ + continue + + issues += checker.check(commit, top_level) + + for issue in issues: + print(issue) + + return len(issues) + + def check_file(top_level, commit, filename, checkers): # Extract the line numbers touched by the commit. commit_diff = commit.get_diff(top_level, filename) @@ -923,9 +947,15 @@ def check_file(top_level, commit, filename, checkers): # Format the file after the commit with all formatters and compute the diff # between the unformatted and formatted contents. after = commit.get_file(filename) + issues = [] formatted = after for formatter in Formatter.instances(filename, checkers): + issues_ = formatter.check_dependencies() + if issues_: + issues += issues_ + continue + formatted = formatter.format(filename, formatted) after = after.splitlines(True) @@ -938,8 +968,12 @@ def check_file(top_level, commit, filename, checkers): formatted_diff = [hunk for hunk in formatted_diff if hunk.intersects(lines)] # Check for code issues not related to formatting. - issues = [] for checker in StyleChecker.instances(filename, checkers): + issues_ = checker.check_dependencies() + if issues_: + issues += issues_ + continue + for hunk in commit_diff: issues += checker.check(after, hunk.side('to').touched) @@ -955,7 +989,7 @@ def check_file(top_level, commit, filename, checkers): print(hunk) if len(issues): - issues = sorted(issues, key=lambda i: i.line_number) + issues = sorted(issues, key=lambda i: getattr(i, 'line_number', -1)) for issue in issues: print(issue) @@ -969,13 +1003,8 @@ def check_style(top_level, commit, checkers): print(title) print(separator) - issues = 0 - # Apply the commit checkers first. - for checker in CommitChecker.instances(commit, checkers): - for issue in checker.check(commit, top_level): - print(issue) - issues += 1 + issues = check_commit(top_level, commit, checkers) # Filter out files we have no checker for. patterns = set() @@ -1047,7 +1076,7 @@ def main(argv): if args.checkers: args.checkers = args.checkers.split(',') - # Check for required dependencies. + # Check for required common dependencies. for command, mandatory in dependencies.items(): found = shutil.which(command) if mandatory and not found: