MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python应用程序静态分析工具推荐

2023-07-072.9k 阅读

Pylint

Pylint是Python中最常用的静态分析工具之一,它能够检查Python代码中的错误、潜在问题以及是否符合编码规范。

安装

在大多数情况下,可以使用pip来安装Pylint:

pip install pylint

基本使用

假设我们有一个简单的Python文件example.py

def add_numbers(a, b):
    return a + b

result = add_numbers(1, 'two')
print(result)

运行Pylint对其进行分析:

pylint example.py

Pylint会指出代码中的问题,例如上面代码中add_numbers(1, 'two')存在类型不匹配的问题,Pylint会给出类似如下的提示:

example.py:5:12: E1101: Instance of'str' has no '+' member (no-member)

配置

Pylint高度可配置,你可以通过创建一个.pylintrc文件来自定义规则。例如,如果你不希望Pylint检查变量名的长度,可以在.pylintrc文件中添加如下配置:

[variables-naming]
max-name-length=0

Flake8

Flake8是另一个流行的Python静态分析工具,它实际上是几个工具的集合,包括pyflakesmccabepep8(现在叫pycodestyle)。

安装

使用pip安装:

pip install flake8

基本使用

同样对于上述example.py文件,运行Flake8:

flake8 example.py

Flake8会指出代码中的语法错误和一些风格问题。例如,对于add_numbers(1, 'two')这种类型不匹配的问题,pyflakes部分会给出提示:

example.py:5:12: F821 undefined name 'two'

这里虽然提示是未定义名称,但本质也是因为类型不匹配导致。同时,Flake8也会检查代码是否符合PEP8编码规范。

插件扩展

Flake8支持通过插件扩展功能。例如,flake8-docstrings插件可以检查函数和类的文档字符串是否符合规范。安装该插件:

pip install flake8-docstrings

然后运行Flake8时,它就会同时检查文档字符串的问题。

Bandit

Bandit主要用于查找Python代码中的安全问题。在开发Web应用、API等涉及安全敏感操作的项目时,Bandit尤为重要。

安装

使用pip安装:

pip install bandit

基本使用

假设有一个包含潜在安全风险的代码文件vulnerable.py

import os
user_input = input("Enter a file name: ")
os.system('rm -rf'+ user_input)

运行Bandit分析该文件:

bandit -r vulnerable.py

Bandit会检测到os.system使用用户输入拼接命令存在命令注入风险,并给出如下提示:

>> Issue: [B605:os_system] Use of potentially dangerous function os.system.
   Severity: High   Confidence: High
   CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
   Location: vulnerable.py:3
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b605_os_system.html
3 | os.system('rm -rf'+ user_input)

配置扫描级别

Bandit支持不同的扫描级别,你可以通过-l参数指定。例如,只扫描高风险问题:

bandit -r -l HIGH vulnerable.py

mypy

mypy是一个Python的静态类型检查工具,对于大型项目和注重类型安全的开发场景非常有用。

安装

使用pip安装:

pip install mypy

基本使用

考虑如下带类型注释的Python代码typed_example.py

def add_numbers(a: int, b: int) -> int:
    return a + b

result = add_numbers(1, 2)
print(result)

运行mypy:

mypy typed_example.py

如果代码类型注释正确且类型使用符合注释,mypy不会输出任何内容。但如果我们修改代码如下:

def add_numbers(a: int, b: int) -> int:
    return a + b

result = add_numbers(1, 'two')
print(result)

运行mypy会给出错误提示:

typed_example.py:5:23: error: Argument 2 to "add_numbers" has incompatible type "str"; expected "int"

配置

mypy可以通过mypy.ini文件进行配置。例如,你可以指定是否严格检查类型,是否忽略某些文件等。以下是一个简单的mypy.ini示例:

[mypy]
strict = True
ignore_missing_imports = True

Pydocstyle

Pydocstyle主要专注于检查Python文档字符串是否符合规范。良好的文档字符串对于代码的可读性和可维护性至关重要。

安装

使用pip安装:

pip install pydocstyle

基本使用

假设有一个包含文档字符串的Python文件docstring_example.py

def multiply_numbers(a, b):
    "Multiply two numbers."
    return a * b

运行pydocstyle:

pydocstyle docstring_example.py

pydocstyle会指出文档字符串的格式问题,例如上面的文档字符串缺少空白行,会给出如下提示:

docstring_example.py:2:1: D203: 1 blank line required between summary line and description; found 0

配置

pydocstyle可以通过.pydocstyle文件进行配置。例如,你可以自定义文档字符串的格式规则。以下是一个简单的配置示例:

[D203]
ignore = True

上述配置表示忽略D203规则,即不检查文档字符串中摘要行和描述之间的空白行。

Radon

Radon是一个用于分析Python代码复杂度的工具。代码复杂度是衡量代码理解和维护难度的一个重要指标。

安装

使用pip安装:

pip install radon

基本使用

假设有一个复杂一点的Python文件complex_example.py

def calculate_factorial(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * calculate_factorial(n - 1)

def is_prime(num):
    if num <= 1:
        return False
    for i in range(2, num):
        if num % i == 0:
            return False
    return True

计算代码的McCabe复杂度:

radon cc complex_example.py

会得到类似如下的输出:

complex_example.py:1:1: 4 calculate_factorial
complex_example.py:6:1: 5 is_prime

这表示calculate_factorial函数的复杂度为4,is_prime函数的复杂度为5。

代码度量

除了复杂度分析,Radon还可以进行其他代码度量,例如计算代码行数(SLOC - Source Lines of Code)。使用如下命令:

radon raw complex_example.py

会输出代码的总行数、空行数和注释行数等信息。

Prospector

Prospector是一个集成式的静态分析工具,它整合了多个其他工具(如Pylint、Flake8等)的功能,并提供统一的输出。

安装

使用pip安装:

pip install prospector

基本使用

对于一个Python项目目录my_project,运行Prospector:

prospector my_project

Prospector会运行多个分析工具,并将结果整合输出。例如,它会同时指出代码中的语法错误、风格问题、潜在安全问题等,就像运行了Pylint、Flake8和Bandit等工具一样,只是输出更加统一和易于查看。

配置

Prospector可以通过prospector.yml文件进行配置。你可以指定要使用的工具、忽略的文件和目录等。以下是一个简单的配置示例:

tools:
  flake8:
    enabled: true
  bandit:
    enabled: false
ignore_patterns:
  - "tests/*.py"

上述配置表示启用Flake8工具,禁用Bandit工具,并忽略tests目录下的所有Python文件。

pyright

pyright是微软开发的一个高性能的Python静态类型检查工具。它特别适用于大型代码库,能够快速准确地检测类型错误。

安装

使用npm安装(因为pyright是基于Node.js开发的):

npm install -g pyright

基本使用

假设有一个Python文件pyright_example.py

def greet(name: str) -> str:
    return "Hello, " + name

result = greet(123)

运行pyright:

pyright pyright_example.py

pyright会指出result = greet(123)这一行存在类型错误,提示如下:

pyright_example.py:4:14 - error: Argument of type "int" cannot be assigned to parameter "name" of type "str" in function "greet"
   "int" is incompatible with "str" (reportGeneralTypeIssues)

配置

pyright可以通过pyrightconfig.json文件进行配置。例如,你可以指定类型检查的严格程度,是否允许隐式类型转换等。以下是一个简单的配置示例:

{
    "typeCheckingMode": "strict",
    "exclude": ["node_modules"]
}

上述配置表示启用严格类型检查模式,并排除node_modules目录。

Semgrep

Semgrep是一个基于模式匹配的静态分析工具,它可以用于查找特定模式的代码,不仅适用于Python,还支持多种其他编程语言。

安装

使用pip安装:

pip install semgrep

基本使用

假设我们要查找代码中所有使用eval函数的地方,因为eval函数存在潜在安全风险。我们可以编写一个Semgrep规则文件rules.yaml

rules:
  - id: dangerous-eval
    pattern: eval(...)
    message: "Using eval can be dangerous"
    languages: [python]
    severity: ERROR

然后运行Semgrep:

semgrep --config rules.yaml your_code_directory

如果代码中有使用eval函数的地方,Semgrep会按照规则给出相应的提示。

规则编写

Semgrep的强大之处在于其灵活的规则编写。你可以编写复杂的模式匹配规则,例如匹配特定函数调用的参数组合等。以下是一个匹配open函数以写入模式打开文件且文件名来自用户输入的规则:

rules:
  - id: dangerous-open-write
    pattern: open($USER_INPUT, 'w')
    message: "Opening a file in write mode with user input can be dangerous"
    languages: [python]
    severity: ERROR

通过这种方式,Semgrep可以帮助我们发现很多特定场景下的潜在问题。

各工具适用场景总结

  1. Pylint和Flake8:适用于日常开发中的代码质量检查,包括语法错误、风格问题等。Pylint更侧重于全面的代码分析和编码规范检查,而Flake8则更轻量级,专注于快速发现常见问题。
  2. Bandit:主要用于安全敏感项目,确保代码没有常见的安全漏洞,如命令注入、SQL注入等。
  3. mypy:对于大型项目和注重类型安全的开发,尤其是在Python 3引入类型注释后,mypy可以在编码阶段发现类型错误,提高代码的稳定性和可维护性。
  4. Pydocstyle:专注于文档字符串的规范检查,对于团队协作开发和代码的长期维护,良好的文档字符串至关重要,Pydocstyle可以确保文档字符串符合规范。
  5. Radon:在代码复杂度管理方面非常有用。通过分析代码复杂度,开发人员可以确定哪些部分需要重构,以提高代码的可理解性和可维护性。
  6. Prospector:适合在项目级别进行全面的静态分析,它整合了多个工具的功能,提供统一的输出,方便开发人员一次性查看多种类型的问题。
  7. pyright:作为高性能的类型检查工具,特别适合大型代码库,能够快速准确地检测类型错误,与mypy类似,但在性能和一些特定功能上可能有所不同。
  8. Semgrep:当需要查找特定模式的代码时,Semgrep非常有用。它可以帮助发现一些自定义规则下的潜在问题,尤其是在代码库较大且需要特定检查的场景。

在实际项目中,通常会根据项目的特点和需求组合使用这些工具。例如,对于一个Web应用项目,可能会同时使用Flake8进行代码风格检查,Bandit进行安全漏洞检测,mypy进行类型检查,以确保代码的质量和安全性。