Paddle icon indicating copy to clipboard operation
Paddle copied to clipboard

【WIP】【Type Hints】Paddle 中引入 Tensor stub 和 Paddle/python/paddle/py.typed 文件

Open megemini opened this issue 1 year ago • 1 comments

PR Category

User Experience

PR Types

Others

Description

关联 PR : https://github.com/PaddlePaddle/Paddle/issues/63597

任务:1-2

根据 https://github.com/PaddlePaddle/Paddle/pull/50211 生成 tensor.pyi

涉及文件:

  • .pre-commit-config.yaml pre-commit 不检查 tensor.pyi
  • python/CMakeLists.txt 编译后执行 gen_tensor_stub.py
  • python/paddle/__init__.py type checking 时引入 tensor 目录下的 Tensor
  • python/paddle/py.typed 标记文件
  • python/setup.py.in 增加 py.typed 和 tensor.pyi
  • setup.py 增加 py.typed 和 tensor.pyi
  • tools/gen_tensor_stub.py 生成 tensor.pyi

目前 (20240428) 还有几个问题:

  • 编译时的命令,gen_tensor_stub.py 加在哪里合适?python/CMakeLists.txt 吗?有这方面规划吗?
  • python/setup.py.insetup.py 都需要增加 py.typed 和 tensor.pyi ?
  • gen_tensor_stub.py 目前只是把 https://github.com/PaddlePaddle/Paddle/pull/50211 里面的拿过来,还需要优化。

megemini avatar Apr 28 '24 11:04 megemini

你的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.

paddle-bot[bot] avatar Apr 28 '24 11:04 paddle-bot[bot]

Update 20240505

gen_tensor_stub.py 利用模板 tensor.pyi.in 生成 tensor.pyi

这里参考 https://github.com/PaddlePaddle/Paddle/pull/50211 中的方法,主要不同:

  • 利用模板,而不是直接生成 stub 文件

    主要逻辑为:在模板中插入 api (signature & docstring),或 api 的 docstring (仅有 signature 没有 docstring 的 api),如果 get_tensor_members 的名称与模板中相同,则优先使用模板中的 signature 和 docstring。

    主要原因为:单使用代码生成 stub 文件,有些地方比较难处理,比如 c++ 中的一些接口,只有简单的方法名称,如果要添加 type 或者 docstring 会比较麻烦,或者比如,有些方法需要 overload (如 init),使用模板也会更简单。

    后续,可以不断完善 tensor.pyi.in 文件,c++ 中不方便修改的,添加到模板中即可,模板的优先级高于 get_tensor_members

    另外,目前 tensor.pyi.in 还不完善,有些 api 的签名还没改,后面需要慢慢完善。

  • 对于 inspect.isdatadescriptor(member) ,当作 @property 处理,而不是 attribute ,主要是由于,这些属性大部分有 docstring,如 data,如果设置成 attribute 就丢失了 ~

  • Tensor 的签名改为 class Tensor(Generic[_ShapeType, _DType]),增加两个类型 shape 和 dtype ,如 def __eq__(self, y: Tensor) -> Tensor[Any, bool] 可以使用,后续也可以方便扩展 ~

另外,没有修改 tensor.py 文件,本来想把 python/paddle/__init__.py 中的 Tensor = framework.core.eager.Tensor 移入 tensor.py 里面,结果发现,目前的源码中有的地方用到了 paddle.Tensor 作为 type annotation,如果移入 tensor.py ,会出现 circular import ,为保险起见做如下处理

if typing.TYPE_CHECKING:
    from .tensor.tensor import Tensor
else:
    Tensor = framework.core.eager.Tensor
    Tensor.__qualname__ = 'Tensor'

python/paddle/tensor/tensor.pyi 为生成的 stub 文件,可供参考 ~

目前,paddle.Tensor 中共有 550 个成员,其中:

  • 添加 478 个方法
  • 添加 10 个 alias
  • 添加 1 个 alias __qualname__ = "Tensor"
  • 不添加 11is_inherited_member
  • 不添加 48 个私有成员
  • 不添加 3 个特殊成员 __array_ufunc____module____new__

以上共计 551 个 (不含 overload)~

@SigureMo 请评审 ~

megemini avatar May 05 '24 06:05 megemini

Sorry to inform you that 770c25b's CIs have passed for more than 7 days. To prevent PR conflicts, you need to re-run all CIs manually.

paddle-ci-bot[bot] avatar May 14 '24 03:05 paddle-ci-bot[bot]

利用模板,而不是直接生成 stub 文件

模板唯一一个问题是不受 Ruff 等工具监控,代码风格无法保证

Tensor 的签名改为 class Tensor(Generic[_ShapeType, _DType]),增加两个类型 shape 和 dtype

我的建议是在想好 API 形态前不要加泛型,一旦现在加了,以后改就是 breaking change,但从非泛型到泛型在非 strict mode 下是兼容性变动

如 def eq(self, y: Tensor) -> Tensor[Any, bool] 可以使用,后续也可以方便扩展 ~

而且这个签名也不对啊,dtype 哪来的 bool

SigureMo avatar May 15 '24 02:05 SigureMo

利用模板,而不是直接生成 stub 文件

模板唯一一个问题是不受 Ruff 等工具监控,代码风格无法保证

是的,所以这个模板文件原则上应该是能通过 mypy 的 python 文件 ~ 而不是 torch 的那种混有 ${xxx} 进行字符串插入的模板 ~

我在线下用 mypy 检查是通过的,后面考虑是否把他也加到 CI 的检查里面?

Tensor 的签名改为 class Tensor(Generic[_ShapeType, _DType]),增加两个类型 shape 和 dtype

我的建议是在想好 API 形态前不要加泛型,一旦现在加了,以后改就是 breaking change,但从非泛型到泛型在非 strict mode 下是兼容性变动

如 def eq(self, y: Tensor) -> Tensor[Any, bool] 可以使用,后续也可以方便扩展 ~

而且这个签名也不对啊,dtype 哪来的 bool

本来是没加泛型的,结果就是因为这个 eq 和 nq 方法,返回的是个 bool 类型的 Tensor ,如果只写 Tensor 感觉语义不明确,所以就加上了 ~ 其他地方暂时没看到需要泛型的,要么就去掉吧?

另外,dtype 有 bool 啊

In [2]: a = paddle.to_tensor(False)

In [3]: a
Out[3]: 
Tensor(shape=[], dtype=bool, place=Place(gpu:0), stop_gradient=True,
       False)

为啥没有???

megemini avatar May 15 '24 03:05 megemini

补充说明一下根据模板 tensor.pyi.in 生成 tensor.pyi 的逻辑:

  • 利用正则在 # annotation: ${xxx} 后面插入 docstring、methods 等
  • 利用正则在 def xxx()... 方法中插入文档
  • 模板中存在定义的方法,如果缺少文档,则只插入文档

megemini avatar May 15 '24 04:05 megemini

另外,关于 tensor.pyi.in 这个模板文件的维护问题,我的想法是,不需要特殊维护,因为,后面 CI 会检查 api 的 typing,如果 tensor 接口有变,且没有办法自动生成有效的签名,那么,如果不修改 tensor.pyi.in 文件手动添加,则 CI 检查 可能 会 fail(没有示例代码和类型测试用例,那就没办法检查了 ... ...)。 这个可以在 《Paddle 中的类型提示》 的开发文档里面写明 ~

megemini avatar May 15 '24 04:05 megemini

另外,dtype 有 bool 啊

这是 paddle.bool 吧,但就算写 paddle.bool 也不对,泛型参数是不能写 paddle.bool 这种「值」的,所以需要设计一种方式覆盖这种表达,但明显目前是不具备的

我在线下用 mypy 检查是通过的,后面考虑是否把他也加到 CI 的检查里面?

如果是合法的 pyi 文件,建议使用 .pyi 后缀,天然所有的检查工具都会检查(black、Ruff 等等)

可以考虑改为 tensor.prototype.pyi 之类的

另外,关于 tensor.pyi.in 这个模板文件的维护问题,我的想法是,不需要特殊维护,因为,后面 CI 会检查 api 的 typing

对的,我不关心这个文件自身的 typing 问题,因为我们本就通过叶子结点(API)去检查了这一点,我这里只是说其它的通用检查工具,以及,编辑器并不会给 .in 提供语法高亮

比如 #63256 就是因为不爽很久了,直接改了后缀

SigureMo avatar May 15 '24 04:05 SigureMo

抱歉,才发现有几个问题没有回复 ~~~

应该是太多了搜索卡住了,但 __ror__ 应该是没有的

我看了一下,dir(paddle.Tensor) 里面确实没有 ~ 你这个 __ror__ 是哪来的?

诶?为啥不从源头删掉?这个文件应该不需要 mypy 检查的

tensor.pyi.in 需要检查啊,不然怎么保证开发者修改这个文件的时候没有改出问题?

另外,对比 https://github.com/cattidea/paddlepaddle-stubs/blob/main/paddle-stubs/_typing/tensor.pyi,以下方法不在 paddle.Tensor 中,进而也不在此次生成的 stub 文件中:

__pos__
__rfloordiv__
__rmod__
__rmatmul__
__ror__
__rand__
__rxor__
__lshift__
__rshift__
__rlshift__
__rrshift__
__abs__
__iter__
__contains__
__dlpack__
__dlpack_device__

这些方法怎么来的?需要手动添加吗?

@SigureMo

megemini avatar May 18 '24 05:05 megemini

另外,对比 https://github.com/cattidea/paddlepaddle-stubs/blob/main/paddle-stubs/_typing/tensor.pyi,以下方法不在 paddle.Tensor 中,进而也不在此次生成的 stub 文件中:

包括 __ror__,这些应该是我觉得「应该」存在的方法,不过这里应该和运行时保持一致,这些不存在的就不要加了

tensor.pyi.in 需要检查啊,不然怎么保证开发者修改这个文件的时候没有改出问题?

检查方式有想好么?可以考虑复用 pre-commit,不建议在 CI 单独开逻辑,会和开发工作流割裂,提完 PR 在 CI 上才发现问题,不了解的同学调试起来也很麻烦

SigureMo avatar May 22 '24 12:05 SigureMo

包括 __ror__,这些应该是我觉得「应该」存在的方法,不过这里应该和运行时保持一致,这些不存在的就不要加了

OK ~

tensor.pyi.in 需要检查啊,不然怎么保证开发者修改这个文件的时候没有改出问题?

检查方式有想好么?可以考虑复用 pre-commit,不建议在 CI 单独开逻辑,会和开发工作流割裂,提完 PR 在 CI 上才发现问题,不了解的同学调试起来也很麻烦

嗯!已经改为 tensor.prototype.pyipre-commit 可以直接检查 ~~~

另外,做以下修改:

  • setup 里面只打包 tensor.pyi,也就是生成的 stub 文件,不打包这个 tensor.prototype.pyi
  • 去掉了 tensor.prototype.pyi 里面的 from __future__ import annotationspre-commit 用来检查的话确实就用不到了 ~

megemini avatar May 22 '24 13:05 megemini

另外,为了更好的 stub 检查,可以考虑引入 Ruff 的 flake8-pyi rules 了~(独立任务)

https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi

SigureMo avatar May 22 '24 13:05 SigureMo

Update 20240523:

  • _typing.basic.py 添加 TensorLike
  • core.pyi 添加 Place
  • 更新 tensor.prototype.pyi

megemini avatar May 23 '24 05:05 megemini