Paddle
Paddle copied to clipboard
【Type Hints】Paddle 的 CI 中引入 mypy 对于 API 中 docstring 的示例代码的类型检查
PR Category
User Experience
PR Types
Others
Description
关联 PR : https://github.com/PaddlePaddle/Paddle/issues/63597
任务:1-4
在 CI 中引入 mypy 对 docstring 中示例代码的检查。与 xdoctest 的检查项共用一个 CI: PR-CI-Static-Check。如果后续有必要的话可以单独开一个 CI 。
涉及文件:
paddle/scripts/paddle_build.sh添加 mypy 检查入口python/unittest_py/requirements.txt添加 mypy 的依赖,这里用最新的 1.10 版本tools/mypy.ini配置文件,后面应该会不断更新tools/type_hints.py使用 mypy 的 api 进行类型检查tools/test_type_hints.py针对type_hinst.py的单测tools/sampcd_processor_utils.py中的get_api_md5判断 api diff 时,考虑 signature 的变动tools/test_sampcd_processor.py修改单测,包括get_api_md5与 适应 0D 后的 xdoctest 检查
用于 type hint 检查的文件:
python/paddle/py.typedpython/setup.py.insetup.py
另外,修改一个文件用于验证:
python/paddle/tensor/math.py
你的PR提交成功,感谢你对开源项目的贡献! 请关注后续CI自动化测试结果,详情请参考Paddle-CI手册。 Your PR has been submitted. Thanks for your contribution! Please wait for the result of CI firstly. See Paddle CI Manual for details.
Update 20240505
将 mypy 的检查引入 PR-CI-Static-Check,通过修改 api 的 annotation 可以触发 mypy 检查 ~
这里需要
- python/paddle/py.typed
- python/setup.py.in
- setup.py`
配合检查 ~
math.py 中修改两个 api
- scale,只修改了 return 的 annotation,ci 中检测到 api 变动,示例代码由于不涉及此 api 的输出检查,因此,此 api 的示例代码通过 mypy 检查
- stanh,为了验证,修改了输入参数,原本是 float ,改为 int ,ci 检查出此 api 变动,并且 mypy 的示例代码检查报错
另外,tools/mypy.ini 还只是最基础的配置,后面应该需要不断更新 ~
@SigureMo 请评审 ~
在 CI 标题里加
[typing_checking=all](后缀形式),则检查全部示例文档
嗯,确实需要,torch 是把所有示例代码抓出来测试的 ~
这个任务可以放到 代码标注 完成之后,目前 get_docstring 可以获取全量 docstring,在 CI 里面加个标记,然后脚本执行的时候把 full_test=True 代进去应该就可以~
def get_docstring(full_test=False):
'''
this function will get the docstring for test.
'''
import paddle
import paddle.static.quantization # noqa: F401
if full_test:
get_full_api_from_pr_spec()
else:
get_incrementapi()
我加个子任务吧?~ https://github.com/PaddlePaddle/Paddle/issues/63597 任务 3-1
另外有一个问题就是,现在这样的话,对于其他不知情的同学来说,在一个示例代码里改了一个 typo CI 是会通过的吗?是否会导致现存的示例代码就通不过呢?
嗯,肯定会有这样的情况 ~
这个 type checking 比之前的示例代码那个任务还麻烦 ~ 其中应该会有一些依赖关系,在没有完成基础标注的情况下,type checking 可能出问题 ~
目前能做的:
- 这个 CI 要等到
_typing和tensor.pyi那两个任务,以及《Paddle 类型提示》这个文档都完成后才能合入,相当于设定一个 milestone,后续 api 修改的时候要添加 type annotation ~ - mypy 的配置中
strict = False,这样可以减少错误的产生,后续如果有必要,可以将这个参数设置为 True ~ - 在类型提示都标注完之前,
PR-CI-Static-Check如果出现 type checking 的 error,可以先找地方(比如之前提到的辅助管理文档或工具)记录下来,然后赦免,最后查缺补漏的时候(或者后面全量检查)再修改 ~
暂时想到这些,@SigureMo 帮忙看看这样行不行?
其余暂时没什么大问题,等 CI 跑完我看下效果~
其实之前就发现一个体验比较差的问题就是,log 太多了,包括示例代码检查,对于大多数工具来说,默认行为是如果 success,那么是静默的,不会在成功的那些 case 上打印过多 log(除非开 --verbose),只有 fail 才会打印详细日志,对于这一点来说我们的日志对于用户是不友好的,之前示例代码还好些吧,加上类型检查就不太可读了,这个可以之后专门优化一下(体验优化,单独 PR)
这个任务可以放到
代码标注完成之后
诶?会不会有点晚?按我理解这个功能可以用来摸底和统计 API 支持情况,感觉可以在批量任务的同时做?
这个 type checking 比之前的示例代码那个任务还麻烦 ~ 其中应该会有一些依赖关系,在没有完成基础标注的情况下,type checking 可能出问题 ~
这样的话,那我们的任务划分也是需要考虑这一点的
这个 CI 要等到 _typing 和 tensor.pyi 那两个任务,以及《Paddle 类型提示》这个文档都完成后才能合入,相当于设定一个 milestone,后续 api 修改的时候要添加 type annotation ~
喔喔,我以为这个想先合呢 :joy:
在类型提示都标注完之前,PR-CI-Static-Check 如果出现 type checking 的 error,可以先找地方(比如之前提到的 辅助管理文档或工具)记录下来,然后赦免,最后查缺补漏的时候(或者后面全量检查)再修改 ~
唔,鉴于这个周期比较长,这会极大影响其他开发者(包括内部的)在这段时间的体验的,而且这会非常麻烦 CI 管理人员,豁免不太合适,是否可以有黑白名单机制呢?比如通过标记
.. code-block:: python
:type-checking: true
但这有一个缺点就是,前期默认是 false,后期默认就是 true 了,后期 true 切默认后最好统一清理掉 :type-checking: true 标记
或者干脆维护一个黑名单,即 API 列表,每完成一个删掉一个,也能根据这个名单直接反映完成情况(不过要做好频繁冲突的准备)
不过两者的话,感觉前者会更灵活些,因为大概率,我们总有一些就是需要禁掉的,这样的 case 直接 :type-checking: false 就好了
其实之前就发现一个体验比较差的问题就是,log 太多了,包括示例代码检查,对于大多数工具来说,默认行为是如果 success,那么是静默的,不会在成功的那些 case 上打印过多 log(除非开 --verbose),只有 fail 才会打印详细日志,对于这一点来说我们的日志对于用户是不友好的,之前示例代码还好些吧,加上类型检查就不太可读了,这个可以之后专门优化一下(体验优化,单独 PR)
现在脚本里是用的 debug,比如 : python sampcd_processor.py --debug --mode cpu; example_error=$?,所以日志比较多 ~
最早之前就这么写,所以示例代码和 type checking 沿用了 ~
那么,去掉 debug?
另外,关于全量 docstring 和黑白名单的问题,昨晚想了想,能不能这样:
- CI 标题中标注
[typing_checking=all],则全量检查 - CI 标题中标注
[typing_checking],则只检查 diff 的 api - CI 中不标注,则不检查 type (默认行为,后面可以改为默认检查)
如果这样:
- 在咱们的标注任务中,可以要求参与的开发者统一写
[typing_checking],只检查修改的 api,也不需要后面再清理示例代码 - 咱们可以在标注任务的几个 milestone,写
[typing_checking=all],全量检查,实时掌握整体情况 - 其他开发者,啥也不标注,也就不检查,没影响
看看这样如何?
另外话说这个是否可以放到根目录的
pyproject.toml呢?现代 python 工具大多配置是放在pyproject.toml里的
可以啊 ~ 我试试看 ~
那么,去掉 debug?
嗯,可以之后试一下
另外,关于全量 docstring 和黑白名单的问题,昨晚想了想,能不能这样:
嗯嗯,不过之后是否有一些情况确实需要禁用掉呢?就像我上面说的「因为大概率,我们总有一些就是需要禁掉的」,是否有方式灵活禁用某些 case 呢?(比如 cpplint 的 // NOLINT、ruff 的 # noqa)
[typing_checking]
喔喔才发现上面写错了 :joy:,应该是 [type_checking],typing_checking 怪怪的
嗯嗯,不过之后是否有一些情况确实需要禁用掉呢?就像我上面说的「因为大概率,我们总有一些就是需要禁掉的」,是否有方式灵活禁用某些 case 呢?(比如 cpplint 的
// NOLINT、ruff 的# noqa)
用 # type: ignore ?比如:
import paddle
a = paddle.aaa # type: ignore
如果不写 # type: ignore,则检查出错:
/home/shun/Documents/Projects/data-projects/paddle_typehint/test_mypy_ignore.py:2: error: Module has no attribute "aaa" [attr-defined]
Found 1 error in 1 file (checked 1 source file)
标记 # type: ignore,则检查成功
Success: no issues found in 1 source file
用 # type: ignore ?比如:
喔喔,局部的这种,可以的,这里说的是整个 case 的情形,如果不至于写太多 type: ignore 的话就没问题
另外可以考虑下示例代码展示时是否要将 type: ignore 删掉~
Update 20240510
-
mypy 的配置放到
pyproject.toml中 -
mypy 的
cache dir使用绝对路径 -
去掉
--debug参数,减少日志输出(不过,由于 CI 日志的特殊性,仍然会输出两次 error 日志) -
根据 PR 的 title 判断是否进行 type checking 或者 type checking all 参考 @gouzil 的实现方式,但是没判断 git log (有点不稳定),只判断 title ~
- title 中有
type checking,则检查 diff 的 api - title 中有
type checking all,则检查全部 api - title 中没有以上两者,则不检查
另外,
paddle_build.sh中的run_type_checking以及if [[ ${run_tc} -eq 0 ]]; then在标注任务结束后就可以去掉了 ~run_type_checking run_tc=$? if [[ ${run_tc} -eq 0 ]]; then { type_checking_info=$(exec_type_checking 2>&1 1>&3 3>/dev/null); } 3>&1 type_checking_code=$? fi summary_check_example_code_problems $[${example_code_gpu} + ${example_code}] "${example_info_gpu}\n${example_info}" if [[ ${run_tc} -eq 0 ]]; then summary_type_checking_problems $type_checking_code "$type_checking_info" fi - title 中有
-
# type: ignore可以用在 codeblock 最开始,或者中间行(单独一行),以规避 codeblock 整体的检查 mypy 本身支持# type: ignore用在文件开头从而避免文件被检查,但是doctest.DocTestParser().get_examples会把单行注释给删除掉,所以我这里单独做了处理,并且支持在 codeblock 中间插入单行# type: ignore,此行之后的也会规避检查>>> # type: ignore >>> # doctest: -REQUIRES(env:GPU) >>> blabla >>> print(1-1) 0 -
其他修改,如修改文件名等
另外,目前这个 PR-CI-Static-Check 是做的全量检查,日志可以保存一下以供参考 ~
@SigureMo 请评审 ~
Sorry to inform you that e7be07d's CIs have passed for more than 7 days. To prevent PR conflicts, you need to re-run all CIs manually.
Sorry to inform you that 618c3b9's CIs have passed for more than 7 days. To prevent PR conflicts, you need to re-run all CIs manually.
Update 20240529
paddle_build.sh中将exec_samplecode_test和exec_type_checking统一放到新增的exec_samplecode_checking中- 检查标题统一改为
typing和typing all - 修改
type_checking.py中的路径表示
另外:
示例代码和类型检查是否可以分别抽一个函数呢?CI 入口一般只做分发,不适合写太多逻辑~ 啊,不对,我才发现是上面的
run_type_checking的语义不对,上面的run_type_checking乍一看是已经开始跑类型检查了,但实际上只是检查了标题,是有点奇怪的,可以改成check_need_type_checking其结果为need_type_checking
这两个问题我一起回复一下 :这里是临时的逻辑,后面会去掉 need_type_checking,也就是默认进行检查,所以这里的逻辑复杂了点 ~
加速效果这么明显的?2 小时直接减少到 10 分钟跑全量?