短毛熊¶
欢迎。本教程旨在向您展示如何使用 @linter
(ref )装饰者,以便在您的熊中集成绒布。
备注
如果您计划创建一只无需包装工具即可执行静电代码分析的熊,请参阅 this link instead .
本教程将带领您完成编写本地林特熊的过程。如果您想要为一个工具编写全局Linter Bear,该工具不会为每个文件运行一次,而是整个项目只运行一次,您仍然可以通过以下地址完成这些步骤,然后阅读全局Linter Bear的不同之处 global_bears .
这为什么有用呢?¶
许多编程语言已经实现了Linters,所以如果您的项目使用的语言还没有Linter Bear,您可能需要自己实现它。别担心,这很容易!
我们需要什么?¶
首先,我们需要要使用的Linter可执行文件。在本教程中,我们将构建PylintTutorialBear,因此我们需要Pylint,它是Python的通用Linter。你可以找到它 here 。因为它是一个python包,所以我们可以使用
$ pip3 install pylint
写“熊”¶
要编写LinterBear,我们需要创建一个与LinterBear基础设施接口的类,该基础设施是通过 @linter
装饰者。
from coalib.bearlib.abstractions.Linter import linter
@linter(executable='pylint')
class PylintTutorialBear:
pass
如你所见 pylint
已经作为可执行文件名提供,将在要链接的文件上调用该可执行文件名。这是装饰者的强制参数。
为此,Linter类一次只能处理一个文件 pylint
或者每次都需要使用适当的参数调用外部工具。这是在里面做的 create_arguments
,
@linter(executable='pylint')
class PylintTutorialBear:
@staticmethod
def create_arguments(filename, file, config_file):
pass
create_arguments
接受三个参数:
filename
:要处理的文件的绝对路径。file
:要处理的文件内容,以行列表的形式给出(包括回车字符)。config_file
:要使用的配置文件的绝对路径。如果未使用配置文件,则此参数为None
。配置文件的处理留给Bear的方法实现。
您可以使用这些参数来构造命令行参数。Linter期望您在这里返回一个参数序列。最好是元组。我们很快就会这么做的 PylintTutorialBear
.
备注
create_arguments
不一定非得是静电的方法。在这种情况下,您还需要将 self
签名中的参数。的某些功能 @linter
仅在实例内部可用,如日志记录。
def create_arguments(self, filename, file, config_file):
self.log("Hello world")
那么,我们需要提供哪些确切的命令行参数呢?这取决于Linter的输出格式。这个 @linter
装饰器能够处理不同的输出格式:
regex
:它解析底层可执行文件生成的问题消息。corrected
:从工具提供的固定/更正文件自动生成结果。unified-diff
:这将从可执行文件提供的统一差异输出中自动生成结果。
在本教程中,我们将使用 regex
输出格式。但是在我们继续修改熊之前,我们需要弄清楚Pylint的输出到底是什么样子,以便我们可以相应地解析它。
在使用以下命令调用Pylint时,我们得到了一些有希望的输出
$ pylint --msg-template="L{line}C{column}: {msg_id} - {msg}" --reports=n
示例输出如下所示:
No config file found, using default configuration
************* Module coalib.bearlib.abstractions.Linter
L1C0: C0111 - Missing module docstring
L42C48: E1101 - Class 'Enum' has no 'reverse' member
L77C32: E1101 - Class 'Enum' has no 'reverse' member
L21C0: R0912 - Too many branches (16/12)
L121C28: W0613 - Unused argument 'filename'
这是我们可以用正则表达式轻松解析的东西。因此,让我们实现我们目前发现的所有内容:
@linter(executable='pylint',
output_format='regex',
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): (?P<message>.*)')
class PylintTutorialBear:
@staticmethod
def create_arguments(filename, file, config_file):
return ('--msg-template="L{line}C{column}: {msg_id} - {msg}"',
'--reports=n', filename)
如你所见, output_regex
参数由命名组组成。这些对于构建包含打印输出信息的有意义的结果非常重要。
有关指定组的确切列表,请参阅 @linter
recognizes, see the API documentation .
请参考 Python3 re module 和 Dive into python 有关正则表达式的信息,请参阅。
让我们温习一下我们的 output_regex
要使用更多信息,请参阅以下内容:
@linter(...
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): '
r'(?P<message>(?P<origin>.\d+) - .*)'),
...)
现在,我们使用问题标识作为来源,因此可以通过代码中的忽略语句停用单个规则。
这个类已经功能齐全,并且允许解析由Pylint产生的问题!
使用Severities¶
Coala使用三种类型的严重度来对结果的重要性进行分类:
INFO
NORMAL
MAJOR
中定义的 coalib.results.RESULT_SEVERITY
。Pylint输出包含我们可以使用的严重性信息:
L1C0: C0111 - Missing module docstring
错误代码前的字母是严重性。为了利用严重性,我们需要在 output_regex
参数使用命名组 severity
:
@linter(...
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): (?P<message>'
r'(?P<origin>(?P<severity>[WFECRI])\d+) - .*)',
...)
所以我们想要接受这些字母所表示的严肃性 W
, F
, E
, C
, R
或 I
。为了使用此严重性值,我们首先必须提供一个映射,该映射获取匹配的严重度字母并将其映射到严重性值 coalib.results.RESULT_SEVERITY
所以科拉明白这一点。这可以通过 severity_map
参数 @linter
:
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
@linter(...
severity_map={'W': RESULT_SEVERITY.NORMAL,
'F': RESULT_SEVERITY.MAJOR,
'E': RESULT_SEVERITY.MAJOR,
'C': RESULT_SEVERITY.NORMAL,
'R': RESULT_SEVERITY.NORMAL,
'I': RESULT_SEVERITY.INFO},
...)
我们可以这样测试我们的熊
$ coala --bear-dirs=. --bears=PylintTutorialBear --files=sample.py
备注
为了使上述命令起作用,我们的当前目录中应该有2个文件: PylintTutorialBear.py
以及我们的 sample.py
。命名是 very 在科拉很重要。科拉将通过它们的熊来寻找熊 文件名 并根据它们的 类的名称 .
通常情况下,不需要提供严重度图,因为Coala有一个默认的严重度图,可以识别许多表示严重度的常用词。有关支持的关键字,请查看API文档!
规格化行号或列号¶
Coala使用基于1的行和列约定,即第一行和第一列是1。但是,一些索引使用基于0的约定。例如, pylint
使用基于1的行约定和基于0的列约定。选项 normalize_line_numbers
和 normalize_column_numbers
可以帮助我们很容易地将林特的会议映射到科拉的会议。他们是 False
默认情况下。如果 normalize_line_numbers
是 True
,行数将增加1。如果 normalize_column_numbers
是 True
,列数将增加1。
注意 pylint
使用从0开始的列约定。我们需要将其映射到Coala的约定,如下所示:
@linter(...
normalize_column_numbers = True,
...)
建议更正使用 corrected
和 unified-diff
输出格式¶
这些输出格式使用起来非常简单,不需要您在熊内部进行进一步设置:
@linter(...
output_format='corrected')
或
@linter(...
output_format='unified-diff')
如果您的底层工具生成已更正的文件或更正的统一差异,该类将自动为所做的更改生成补丁,并相应地生成结果。
将设置添加到我们的熊¶
如果我们逃跑
$ pylint --help
我们可以看到有一个 --rcfile
选项,该选项允许我们为Pylint指定配置文件。让我们将该功能添加到我们的熊中。
import os
from coalib.bearlib.abstractions.Linter import linter
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
@linter(executable='pylint',
output_format='regex',
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): '
r'(?P<message>(?P<severity>[WFECRI]).*)',
severity_map={'W': RESULT_SEVERITY.NORMAL,
'F': RESULT_SEVERITY.MAJOR,
'E': RESULT_SEVERITY.MAJOR,
'C': RESULT_SEVERITY.NORMAL,
'R': RESULT_SEVERITY.NORMAL,
'I': RESULT_SEVERITY.INFO})
class PylintTutorialBear:
@staticmethod
def create_arguments(filename, file, config_file,
pylint_rcfile: str=os.devnull):
return ('--msg-template="L{line}C{column}: {msg_id} - {msg}"',
'--reports=n', '--rcfile=' + pylint_rcfile, filename)
只需将所需的参数添加到 create_arguments
签名就足够了,就像你对里面的其他熊所做的那样 run
好了!从Coafile中自动查询附加参数。我们还添加一些文档以及元数据属性:
@linter(...)
class PylintTutorialBear:
"""
Lints your Python files!
Checks for coding standards (like well-formed variable names), detects
semantical errors (like true implementation of declared interfaces or
membership via type inference), duplicated code.
See http://pylint-messages.wikidot.com/all-messages for a list of all
checks and error codes.
"""
@staticmethod
def create_arguments(filename, file, config_file,
pylint_rcfile: str=os.devnull):
"""
:param pylint_rcfile:
The configuration file Pylint shall use.
"""
...
备注
参数的文档由Coala解析,它将被用作该特定设置的用户帮助。
成品熊¶
干得好,你走到这一步了!现在您应该已经构建了一个功能齐全的Python Linter Bear。如果您遵循本教程中的代码,它应该如下所示
import os
from coalib.bearlib.abstractions.Linter import linter
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
@linter(executable='pylint',
output_format='regex',
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): '
r'(?P<message>(?P<severity>[WFECRI]).*)',
severity_map={'W': RESULT_SEVERITY.NORMAL,
'F': RESULT_SEVERITY.MAJOR,
'E': RESULT_SEVERITY.MAJOR,
'C': RESULT_SEVERITY.NORMAL,
'R': RESULT_SEVERITY.NORMAL,
'I': RESULT_SEVERITY.INFO})
class PylintTutorialBear:
"""
Lints your Python files!
Checks for coding standards (like well-formed variable names), detects
semantical errors (like true implementation of declared interfaces or
membership via type inference), duplicated code.
See http://pylint-messages.wikidot.com/all-messages for a list of all
checks and error codes.
https://pylint.org/
"""
@staticmethod
def create_arguments(filename, file, config_file,
pylint_rcfile: str=os.devnull):
"""
:param pylint_rcfile:
The configuration file Pylint shall use.
"""
return ('--msg-template="L{line}C{column}: {msg_id} - {msg}"',
'--reports=n', '--rcfile=' + pylint_rcfile, filename)
添加元数据属性¶
现在我们需要给我们的熊添加一些更有价值的信息。这有助于提供有关每个熊的更多信息,还有助于某些函数通过使用这些值来收集信息。我们的熊现在看起来像是:
import os
from coalib.bearlib.abstractions.Linter import linter
from dependency_management.requirements.PipRequirement import PipRequirement
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
@linter(executable='pylint',
output_format='regex',
output_regex=r'L(?P<line>\d+)C(?P<column>\d+): '
r'(?P<message>(?P<severity>[WFECRI]).*)',
severity_map={'W': RESULT_SEVERITY.NORMAL,
'F': RESULT_SEVERITY.MAJOR,
'E': RESULT_SEVERITY.MAJOR,
'C': RESULT_SEVERITY.NORMAL,
'R': RESULT_SEVERITY.NORMAL,
'I': RESULT_SEVERITY.INFO})
class PylintTutorialBear:
"""
Lints your Python files!
Checks for coding standards (like well-formed variable names), detects
semantical errors (like true implementation of declared interfaces or
membership via type inference), duplicated code.
See http://pylint-messages.wikidot.com/all-messages for a list of all
checks and error codes.
https://pylint.org/
"""
LANGUAGES = {'Python', 'Python 2', 'Python 3'}
REQUIREMENTS = {PipRequirement('pylint', '1.*')}
AUTHORS = {'The coala developers'}
AUTHORS_EMAILS = {'coala-devel@googlegroups.com'}
LICENSE = 'AGPL-3.0'
CAN_DETECT = {'Unused Code', 'Formatting', 'Duplication', 'Security',
'Syntax'}
SEE_MORE = 'https://pylint.org/'
@staticmethod
def create_arguments(filename, file, config_file,
pylint_rcfile: str=os.devnull):
"""
:param pylint_rcfile:
The configuration file Pylint shall use.
"""
return ('--msg-template="L{line}C{column}: {msg_id} - {msg}"',
'--reports=n', '--rcfile=' + pylint_rcfile, filename)
备注
对于语言命名,请始终考虑相应Wikipedia页面中使用的格式(例外情况:使用CPP,而不是CPP或C++)。
如果该定义尚不存在,则在编写新的Bear时应添加语言定义。
语言中使用的别名应该是Coala定义中的名称之一。
运行和测试我们的熊¶
通过运行
$ coala --bear-dirs=. --bears=PylintTutorialBear -B
我们可以看到,我们的Bear设置已正确记录。在启用熊的情况下使用Coala sample.py 我们跑
$ coala --bear-dirs=. --bears=PylintTutorialBear --files=sample.py
要使用我们的 pylint_rcfile 设置我们可以做的
$ coala --bear-dirs=. --bears=PylintTutorialBear \
> -S pylint_rcfile=my_rcfile --files=sample.py
现在您知道了如何编写一个Linter Bear,以及如何在您的项目中使用它。
祝贺你!
全球短毛熊¶
有些linting工具不是在文件级运行,即每个文件运行一次,而是在项目级运行。它们可能会检查目录结构的某些属性,或者只检查一个特定的文件,如 setup.py
.
对于这些工具,我们需要一个 GlobalBear
我们还可以使用 @linter
给我们一个,通过传递参数 global_bear=True
:
from coalib.bearlib.abstractions.Linter import linter
@linter(executable='some_tool',
global_bear=True,
output_format='regex',
output_regex=r'<filename>: <message>')
class SomeToolBear:
@staticmethod
def create_arguments(config_file):
return []
这个 create_arguments
方法不接受 filename
和 file
在这种情况下,因为没有文件上下文。您仍然可以让Coala知道在其中检测到问题的文件,方法是使用 filename
您的 output_regex
如果与缠绕的工具相关,请执行以下操作。
如前所述, create_arguments
不一定非得是静电的方法。在这种情况下,请记住预加 self
签名中的参数:
from coalib.bearlib.abstractions.Linter import linter
@linter(executable='some_tool',
global_bear=True,
output_format='regex',
output_regex=r'<filename>: <message>')
class PythonTestBear:
def create_arguments(self, config_file):
return '--lint', self.file_dict.keys()
您可以使用以下命令访问完整的文件列表 self.file_dict
,它返回一个 {{filename: file contents}}
。请注意,当命令行的长度超过操作系统允许的长度,或者完整的文件列表大于操作系统允许的参数数量时,将文件的完整列表放在命令行上会导致中断。有关更多信息,请查看 here .
哪里可以找到更多的..。¶
如果您需要更多有关 @linter
decorator, refer to the API documentation .