一开始,让我们澄清一个重要的区别。写好代码和写好代码是两件非常不同的事情。前者是一种技能,而后者是一种艺术形式,这种区别将伟大的程序员与人群区分开来。
当我们谈论好的代码时,"好 "这个词在设计上是模糊的。这是因为对于什么是好的或坏的代码,没有既定的规则。我们所拥有的只是一些抽象的准则,比如说可读性。
程序的目的是供人阅读,而只是顺便供计算机执行。
--阿贝尔森和苏斯曼
这是两位麻省理工学院的教授,他们的资历相当雄厚。识别代码的质量是一种直觉,是通过实践和经验长期磨练出来的。代码审查对实现这一目标有很大的帮助。
代码审查是一个过程,在这个过程中,比你更有经验的开发人员会阅读你的代码,并提出可以使其更好的改进建议。这些建议可以提高性能,纳入新的语言功能,修补安全疏忽,或纠正代码风格。
但人工代码审查是很昂贵的。人们花在阅读你的代码上的时间,是他们没有花在构建令人敬畏的东西上的时间。他们也很容易出错,而且绝非全面。人类的知识和记忆是有限的。
进入自动化。自动化的代码审查比人工审查更快,更不容易出错,而且更深入。
让我们通过一个包含单一Python文件的样本项目深入了解这个过程。我们将在该文件中找出问题,然后建立一个工作流程,自动发现并修复这些问题。
成分
A.有违反PEP-8的代码库
在我们自动进行代码审查之前,我们先写一些代码来审查。这里有一个示例程序,它列出了一个给定数字以内的素数。看一下可能会很疼,这很好,因为这意味着你的代码嗅觉正在发挥作用。
# list_primes.py
def is_prime (number):
for i in range(2, number):
if number%i==0:
return False
return True
def list_primes(upper):
for number in range(2, upper):
if is_prime(number):
print(F"{number} is prime")
list_primes(10)
这个脚本有很多问题。不要误会我的意思,它可以工作,但它并不好:
- 函数括号前有多余的空格
- 2个空格的缩进
- 操作符周围没有空格
- 函数周围有单行
- f字符串中的大写字母
F
B.Black代码格式化器
Black是一个流行的Python代码格式化器。它能够自动重新格式化你的Python文件,修复所有违反代码风格的行为。最整洁的是,它相当有主见,而且不能进行太多的配置,使它成为自动化的理想选择。
所以让我们安装Black,同时也利用这个机会用我个人喜欢的工具Pipenv来设置一些一流的依赖性管理。运行下面的命令,在 repo 的根目录下创建两个文件,Pipfile
和Pipfile.lock
,并将 Black 安装为开发依赖项。
$ pipenv install --pre --dev black
在没有任何参数的情况下运行Black,可以直接格式化 repo中的所有文件。除了重新格式化你的文件外,它还有两种不太危险的模式。
--check
:在这种模式下,Black纯粹是检查是否有任何违反代码风格的情况。如果没有违规,返回代码为0,如果有,则为非零。
$ pipenv run black --check list_primes.py
would reformat /Users/dhruvkb/Documents/scratch/automata/list_primes.py
Oh no! 💥 💔 💥
1 file would be reformatted.
--diff
:在这种模式下,Black显示它将进行的修改,而不实际进行修改。如果你想在实际进行修改之前检查这些修改,这种模式很有帮助。
$ pipenv run black --diff list_primes.py
--- list_primes.py 2020-02-02 00:00:00.000000 +0000
+++ list_primes.py 2020-02-02 00:00:00.000000 +0000
@@ -1,17 +1,19 @@
# list_primes.py
-def is_prime (number):
+def is_prime(number):
- for i in range(2, number):
+ for i in range(2, number):
- if number%i==0:
+ if number % i == 0:
- return True
+ return True
- return False
+ return False
+
def list_primes(upper):
for number in (2, 10):
if is_prime(number):
- print(F"{number} is prime")
+ print(f"{number} is prime")
+
list_primes(15)
配方
代码审查可以分为两部分:有趣的部分是解决大的问题,平凡的部分是识别非习惯性片段和违反代码风格。让我们把代码审查的无聊部分自动化。
1.设置Git钩子
我们刚刚看到Black是多么不可思议。如果Black在你每次提交代码时自动运行,那不是很好吗?这是有可能的,因为有了Git钩子。Git 钩子是当你执行某些 Git 命令时在你的代码库中运行的程序。我们对 "pre-commit "钩子特别感兴趣,因为我们希望在提交前进行lint检查,并在检查失败时阻止提交的产生。
Autohooks是一个Python包,用于通过Python管理这些钩子。它有一个插件系统,可以与Black等工具集成。让我们同时安装Autohooks和Black-integration插件。
$ pipenv install --dev autohooks autohooks-plugin-black
在你的 repo 根目录下做一个pyproject.toml
文件,内容如下。
[tool.autohooks]
mode = "pipenv"
pre-commit = ["autohooks.plugins.black"]
[tool.autohooks.plugins.black]
arguments = ["--check"]
激活钩子并运行检查功能,看看是否一切正常。
$ pipenv run autohooks activate
✓ autohooks pre-commit hook installed at /Users/hal/Documents/scratch/.git/hooks/pre-commit using pipenv mode.
$ pipenv run autohooks check
✓ autohooks pre-commit hook is active.
✓ autohooks pre-commit hook is up-to-date.
ℹ Using autohooks mode "pipenv".
✓ Plugin "autohooks.plugins.black" active and loadable.
试试git commit
-ing写得不好的源代码。哈,找到你了!下面是事情将如何进行的:
- 预提交钩子将被启动。
- Autohooks的顶层钩子将被调用。
- 然后,Autohooks将执行Black,参数为
--check
。 - Black将返回一个非零代码,因为该文件包含错误。
- Git将停止提交操作。
$ git add . && git commit -m "Add the source code to VCS"
ℹ autohooks => pre-commit
ℹ Running autohooks.plugins.black
would reformat /Users/dhruvkb/Documents/scratch/automata/list_primes.py
Oh no! 💥 💔 💥
1 file would be reformatted.
×
💡专业建议: 你可以用--no-verify
标志绕过钩子,在git commit
。不推荐这样做,但我们不是警察,所以做你想做的。
💡 建议: 你可以删除--check
参数,然后每次提交时,Black都会为你重新格式化你的文件。
2.使用GitHub动作进行Lint检查
Git 钩子的主要缺点是它们是本地的。在一个有多个贡献者的项目中,可能会有人忘记激活钩子或主动试图绕过钩子。在这种情况下,解决方案是在远程 repo 上运行 lint 检查。GitHub Actions为运行lint提供了一个非常通用的解决方案。
在 repo 的.github/workflows
目录下创建文件lint.yml
。
# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: actions/setup-[email protected]
- uses: psf/[email protected]
这个工作流程会检查出仓库,设置Python,安装Black,然后对文件进行检查。默认情况下,这个动作运行Black,参数为--check
和--diff
。一旦你设置好了lintsing,所有未来的提交和PR都会通过Black。
我们的list_primes.py
文件将无法通过测试。日志将显示失败的文件以及这些文件的差异(因为有-diff
这个参数)。这在你修复违规的时候会很方便。
3.使用GitHub工作流来进行修正
这给我们带来了一个我们仍未解决的方面。Black能够对文件进行重新格式化,但到目前为止,我们只用它来检测问题和呈现差异。我们还没有挖掘出Black的全部潜力。
我们把自动化程度提高到11,并更新我们的GitHub工作流程,以自动修复代码风格违规,如何?
# .github/workflows/lint.yml
name: Lint
on:
- push
- workflow_dispatch
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: actions/setup-[email protected]
- name: Install Python dependencies
run: |
pip install pipenv
pipenv install --deploy --dev --system
- uses: wearerequired/lint-[email protected]
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
black: true
auto_fix: true
这个工作流程使用了与之前相同的两个步骤,那就是检查 repo 和设置 Python。然后我们安装Pipenv,用它来在系统上安装Black。lint-action
,运行Black,然后提交修改后的文件。这就创建了一个新的提交,与Black在diff中显示的改动相同!
💡 专业提示: 你可以自定义作者的名字和电子邮件,也可以自定义提交的信息。只需在动作的with
关键处添加以下内容。这是个发挥创意的机会!
commit_message: 'Opening the pod-bay doors'
git_name: 'HAL-9000'
git_email: '[email protected]'
现在你可以保证推送到 repo 的代码不存在风格指南的偏差,这让代码审查员可以从大局出发来审视代码,而把细枝末节留给 HAL。
使用DeepSource的端到端自动化
吁!这可是个大工程啊。这是很重要的工作,不是吗?但你猜怎么着,我们现在只看了违反风格指南的情况。如果增加更多的功能,比如寻找安全漏洞、发现可能的bug以及进行复杂的重构,这将是一项非常漫长的工作。但它不一定是这样。
你也可以考虑用DeepSource将整个审计、审查和重构过程自动化,它可以在每次提交时扫描你的代码,对于每次拉动请求,通过几个工具(包括linters和安全分析器),可以自动修复许多问题。DeepSource还有它为大多数语言定制的分析器,这些分析器不断得到改进并保持最新状态。
它的设置令人难以置信的简单!你只需要在你的版本库根部添加一个.deepsource.toml
文件,DeepSource就会接收它。这比我们刚才所做的努力要少得多。
version = 1
[[analyzers]]
name = "python"
enabled = true
[analyzers.meta]
runtime_version = "3.x.x"
max_line_length = 80
[[transformers]]
name = "black"
enabled = true
完成加冕大典
对于新的开发者来说,代码审查是一个非常重要的学习工具。它是一种将知识、经验和惯例从资深开发者传授给初级开发者的方式,是一种了解即使是被认为是最后的代码也可以变得更好、更干净、更有效的方式。
我甚至敢说,代码审查是开发人员最好的学习工具之一,而它们被浪费在像代码风格这样的平凡事物上。引入一点自动化,让每一次代码审查都有意义。
他们知道足够的人知道如何学习。
如果做得好,代码审查可以是一个真正的教育经历。自动化不能取代这一点。自动化所能做的是将平凡的过程去掉。