[API Compatibility] enhance paddle.enable_compat -part#79391
Conversation
risemeup1111
left a comment
There was a problem hiding this comment.
发现了需要修复后再合入的兼容性回归,具体问题已放在行级评论中:主要是 paddle.compat 的 lazy attribute 行为被破坏,以及已有的 compat public API 被删除。CI 当前仍有部分任务进行中,且已有 Windows-GPU build/test 失败,请后续一并确认。
| def __getattr__(name): | ||
| if name == "paddle_triton": | ||
| return paddle_triton_fun() |
There was a problem hiding this comment.
这里删掉了 distributions 的 lazy import 和最终的 AttributeError,导致两个回归:paddle.compat.distributions 在未预先 import paddle.compat.distributions 时会通过 __getattr__ 直接返回 None,未知属性也不再抛 AttributeError,会破坏 Python 模块属性协议以及已有的 paddle.compat.distributions 访问方式。请保留 distributions 分支,并让未知属性继续抛 AttributeError。
| def __getattr__(name): | |
| if name == "paddle_triton": | |
| return paddle_triton_fun() | |
| def __getattr__(name): | |
| if name == "paddle_triton": | |
| return paddle_triton_fun() | |
| if name == "distributions": | |
| import importlib | |
| module = importlib.import_module("paddle.compat.distributions") | |
| globals()[name] = module | |
| return module | |
| raise AttributeError(f"module {__name__!r} has no attribute {name!r}") |
| 'BatchNorm1d', | ||
| 'BatchNorm2d', | ||
| 'BatchNorm3d', | ||
| 'MultiheadAttention', |
There was a problem hiding this comment.
这里把 paddle.compat.nn.BatchNorm1D/2D/3D、小写别名以及 SmoothL1Loss 从 public compat surface 删除了,但这些 API 已经有现有调用和测试覆盖:test/legacy_test/test_api_compatibility_part1.py 在类定义时直接绑定 paddle.compat.nn.BatchNorm*,test/legacy_test/test_compat_smooth_l1_loss.py 也直接实例化 paddle.compat.nn.SmoothL1Loss。删除后这些测试会在 import/执行阶段变成 AttributeError,也会破坏已有用户代码。请恢复这些符号及其实现;如果目标是只做命名空间 alias,仍需要保留 compat 侧的实现作为 paddle.* alias 的目标。
建议形状:
__all__ = [
# ...
'BatchNorm1D', 'BatchNorm2D', 'BatchNorm3D',
'BatchNorm1d', 'BatchNorm2d', 'BatchNorm3d',
'SmoothL1Loss',
]
class BatchNorm1D(nn.BatchNorm1D):
... # 保留 torch-style eps/momentum/track_running_stats 适配
BatchNorm1d = BatchNorm1D
# 同步恢复 2D/3D 以及 SmoothL1Loss| return x | ||
|
|
||
| return paddle.nn.functional.unfold( | ||
| return _native(paddle.nn.functional, "unfold")( |
There was a problem hiding this comment.
这里删除 paddle.compat.nn.functional.smooth_l1_loss 会直接破坏已有 compat functional API:test/legacy_test/test_compat_smooth_l1_loss.py 覆盖了 F_compat.smooth_l1_loss(..., beta=...)、target=、size_average/reduce、beta=0 和 forbidden keywords。删除后这些用例和用户迁移代码都会变成 AttributeError,并且 paddle.enable_compat() 也无法把 paddle.nn.functional.smooth_l1_loss alias 到 torch-style 语义。请恢复该函数、__all__ 条目,以及所需的 warnings 和 _ReduceMode。
建议形状:
import warnings
if TYPE_CHECKING:
_ReduceMode: TypeAlias = Literal["mean", "sum", "none"]
__all__ = [
# ...
'unfold',
'smooth_l1_loss',
]
@ForbidKeywordsDecorator(
illegal_keys={"label", "delta", "is_huber", "name"},
func_name="paddle.compat.nn.functional.smooth_l1_loss",
correct_name="paddle.nn.functional.smooth_l1_loss",
)
def smooth_l1_loss(input, target, size_average=None, reduce=None, reduction='mean', beta=1.0):
# 保留原来的 torch-style beta/size_average/reduce 适配逻辑
...
CI 分析结果本轮达到时间上限,未完成全部失败原因分析;未分析完的 job 标记为“分析省略/待后续深挖”。 |
liuhao2638
left a comment
There was a problem hiding this comment.
已复查当前提交。此前指出的 paddle.compat lazy attribute、compat.nn BatchNorm/SmoothL1Loss、compat.nn.functional.smooth_l1_loss 删除问题在当前代码中已经恢复。
不过这轮发现了一个新的全局 alias 下的 P1 回归,具体见行级评论:smooth_l1_loss wrapper 需要通过 native 实现回调,避免 enable_compat() 后自引用到 compat wrapper。CI 目前仍有任务运行中,后续也请一并确认。
| for attr_name in getattr(compat_module, PUBLIC_ATTR_DECLARATION): | ||
| if attr_name.startswith("_"): | ||
| continue | ||
| compat_attr = getattr(compat_module, attr_name) |
There was a problem hiding this comment.
这里会把每个 paddle.compat.*.__all__ 符号 alias 到真实 paddle.* namespace,因此全局 paddle.enable_compat() 后 paddle.nn.functional.smooth_l1_loss 也会变成 compat wrapper。当前 wrapper 在函数尾部仍直接调用 paddle.nn.functional.smooth_l1_loss(..., delta=beta, is_huber=False);alias 生效后这个名字已经指回 wrapper 自身,ForbidKeywordsDecorator 会拒绝 delta/is_huber(或进入自调用路径),导致迁移代码里的 paddle.nn.functional.smooth_l1_loss(x, y, beta=...) 失败。
请像 unfold/SDPA 一样通过 _native 调回 Paddle 原生实现,并补一个 enable_compat() 下的回归用例覆盖这个别名路径。建议形状:
if beta == 0:
return _native(paddle.nn.functional, "l1_loss")(
input, target, reduction=reduction
)
return _native(paddle.nn.functional, "smooth_l1_loss")(
input, target, reduction=reduction, delta=beta, is_huber=False
)|
/re-run all-failed |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #79391 +/- ##
==========================================
Coverage ? 95.48%
==========================================
Files ? 6
Lines ? 133
Branches ? 0
==========================================
Hits ? 127
Misses ? 6
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
|
||
|
|
||
| @cache | ||
| def _register_compat_override(): |
There was a problem hiding this comment.
如果 paddle.compat.* 已经patch到 paddle.* 上了,这里还需要去额外操作吗
| # disable_compat() on exit would remove the finder and destroy the | ||
| # surrounding scoped enable (the runtime entry point of the migration | ||
| # workflow). So restore the finder to exactly how it was found. | ||
| if already_has_torch_proxy: |
| public_attrs = getattr(module, PUBLIC_ATTR_DECLARATION) | ||
| torch_module_name = module_info.name.replace( | ||
| PADDLE_PREFIX, TORCH_PREFIX, 1 | ||
| # Use the root-inclusive iterator: pkgutil.walk_packages skips the |
There was a problem hiding this comment.
这些注释清理一下吧,如果确需要注释的话,也只是简短意赅的关键1~2行,不用长篇大论的写
| _restore_paddle_namespace_aliases() | ||
|
|
||
|
|
||
| def enable_compat( |
There was a problem hiding this comment.
直接改这个可能有点不兼容,加个level参数吧
背景这个主要是解决当前的paddle框架中无法完全对齐的问题,当前Paddle API的对齐状态分为以下三种:
用户在编写paddle时,很难知道哪些是对齐的1,哪些是未对齐的2/3,这使得编写paddle代码成本仍较高。 开发思路目前考虑在enable_compat最后面加一个level参数,默认为1,对于:
针对 第三方库+存量Paddle代码 的用法:level=1,部分API无法对齐,需手动检查调整,主要为避免存量paddle代码出现不兼容 针对 第三方库+新增Paddle代码 的用法:level=2,完全对齐,统一按torch的形态即可 @SigureMo 看下这样的设计思路怎么样,有没有要调整的地方?比如 不加level直接按2来、加level但默认为2、新增一个其他API来作为这个开关不用放到enable_compat里? |
SigureMo
left a comment
There was a problem hiding this comment.
针对 第三方库+存量Paddle代码 的用法:level=1,部分API无法对齐,需手动检查调整,主要为避免存量paddle代码出现不兼容
针对 第三方库+新增Paddle代码 的用法:level=2,完全对齐,统一按torch的形态即可
我比较赞同这里的 level=1/2 的方案,主要是考虑到现存代码可能有部分已经使用 Paddle 原生 API 改写过了,此时一旦自动直接将这些 API 迁移到 paddle.compat API 实现可能会出现一些因为兼容性导致的非预期的问题,直接按 2 来我倒是有些担忧的,不过倒是后面可以考虑把 2 切默认(理想情况)
不过值得注意的是,paddle.enable_compat 这个 API 真正的挑战并不在于实现这些映射功能,而在于当环境中真的安装有 PyTorch 时,如何避免发生冲突(CI 中几乎测不到,只有几个使用 fake torch 模拟的 case),这是真实业务场景中会使用的,因此在实现时候需要特别注意下
| return x | ||
|
|
||
| return paddle.nn.functional.unfold( | ||
| return _native(paddle.nn.functional, "unfold")( |
There was a problem hiding this comment.
我有个疑问,既然是 patch,那么一旦开启 compat,所有相关 API 都会变吧?为什么当前改动仅限于 paddle.compat 下?非 compat 下的 API 理论上也会受到影响吧?(只是问题并不是像递归这么明显罢了)
当然不是让你把所有 API 改一遍,而是我在想这样做的可行性
There was a problem hiding this comment.
其实这也很好做,改造一下:
from typing import Callable, TypeVar
from typing_extensions import ParamSpec
from contextlib import contextmanager
T1 = TypeVar("T1")
T2 = TypeVar("T2")
P1 = ParamSpec("P1")
P2 = ParamSpec("P2")
COMPAT_API_ENABLED = False
def is_compat_api_enabled():
return COMPAT_API_ENABLED
@contextmanager
def compat_api_guard(enable: bool = True):
global COMPAT_API_ENABLED
if enable == COMPAT_API_ENABLED:
yield
return
original_value = COMPAT_API_ENABLED
COMPAT_API_ENABLED = enable
try:
yield
finally:
COMPAT_API_ENABLED = original_value
# compat/registry.py
def dispatch_compat_api(
compat_fn: Callable[P1, T1],
) -> Callable[[Callable[P2, T2]], Callable[P2, T2]]:
def native_fn_decorator(native_fn: Callable[P2, T2]) -> Callable[P2, T2]:
def maybe_use_compat_api(*args: P2.args, **kwargs: P2.kwargs) -> T2:
if is_compat_api_enabled():
# 关键在这里,灵活关掉 compat
return compat_api_guard(enable=False)(compat_fn)(*args, **kwargs) # type: ignore
return native_fn(*args, **kwargs)
return maybe_use_compat_api
return native_fn_decorator
def unfold_compat(x, y):
return unfold(x, y) + 1
@dispatch_compat_api(unfold_compat)
def unfold(x, y):
return x * y
with compat_api_guard():
print(unfold(1, 2)) # 3
with compat_api_guard(enable=False):
print(unfold(1, 2)) # 2随便搞的原型,仅供参考
| paddle.enable_compat() | ||
| try: | ||
| self.assertEqual(paddle.min(t).item(), 1.0) | ||
| self.assertEqual(paddle.max(t).item(), 3.0) | ||
| # dim form returns a namedtuple (values, indices) | ||
| r = paddle.min(t, dim=0) | ||
| self.assertEqual(r.values.item(), 1.0) | ||
| self.assertEqual(r.indices.item(), 1) | ||
| finally: | ||
| paddle.disable_compat() |
There was a problem hiding this comment.
这种单测不能直接用 @use_compat_guard 装饰器么?
There was a problem hiding this comment.
@Manfredss 对于整个框架内部,调用到这些compat系列API的地方(约30个),确实应该都是paddle naive版的,而这一系列API对外呈现的则是torch版的。
这个主要影响到的是 单测 + 部分调用paddle API的组合实现API。
可能得加大一下测试面,比如level=2下,paddle其他的API还能否跑过,除了paddle自身测试外,建议在paconvert里也进行测试(全局设置为level=2),测量全量API的行为是否正常。
|
paddle.enable_compat 增加 level 参数: 背景:Paddle API 对齐分三类:
设计:
level=2 关键实现
测试
|
|
/re-run all-failed |
There was a problem hiding this comment.
Pull request overview
This PR extends paddle.enable_compat() with a new compatibility “level=2” mode that (when enabled globally) aliases public paddle.compat.* APIs onto the live paddle / paddle.nn / paddle.nn.functional namespaces, using caller-aware dispatch to keep Paddle internal calls on native implementations. It also adds guards to avoid recursion when compat wrappers call back into aliased paddle.* APIs, and introduces comprehensive tests for aliasing + lifecycle behavior.
Changes:
- Add a level-based compat mode where global
enable_compat(level=2)aliasespaddle.*topaddle.compat.*using caller-aware dispatchers/proxies (and restores exactly on disable). - Add
compat_api_guard(enable=False)usage in compat wrappers to ensure internal calls hit native implementations under aliasing. - Add new test coverage (including fake modules) for namespace aliasing, scoped/global lifecycle behavior, and torch root API mapping.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/compat/test_compat_namespace_aliased.py | New end-to-end tests for level=2 aliasing, restoration, lifecycle correctness, and caller-aware dispatch behavior. |
| test/compat/fake_modules/torch_proxy_root_api_module.py | Fake module used to validate scoped imports + torch root API capture behavior. |
| python/paddle/compat/proxy.py | Implements level=2 namespace aliasing, caller-aware dispatch, compat suspension guard, root-package override coverage, and improved guard restoration logic. |
| python/paddle/compat/nn/transformer.py | Applies compat_api_guard(enable=False) to internal paths that must remain native under aliasing. |
| python/paddle/compat/nn/functional/sdpa.py | Guards compat SDPA implementation so its internals consistently use native implementations. |
| python/paddle/compat/nn/functional/init.py | Guards compat unfold to ensure internal calls remain native under level=2 aliasing. |
| python/paddle/compat/nn/init.py | Guards compat AvgPool* forward; adjusts Softmax.extra_repr (but currently introduces a bug). |
| python/paddle/compat/init.py | Exposes compat dispatch/guard helpers and guards several top-level compat functions to prevent recursion under aliasing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def extra_repr(self) -> str: | ||
| return f"dim={self._dim}" | ||
| return f"dim={self.dim}" | ||
|
|
| # (live module, attr name) -> original value (or ``_MISSING``) for every | ||
| # ``paddle.*`` symbol aliased to its ``paddle.compat.*`` counterpart, so the | ||
| # paddle namespace can be restored exactly on ``disable_compat()``. | ||
| _PADDLE_NAMESPACE_SAVED: dict[tuple[types.ModuleType, str], Any] = {} | ||
|
|
|
/re-run all-failed |
|
/re-run all-failed |
|
/re-run all-failed |
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前提交。上次的 root compat-only torch.slogdet 暴露问题已通过 level=2 专用路径补上,level=1 行为未被改动;本轮没有新增行级评论。
-
优先级:P2 非行级:跨仓验证不在 changed diff line 上。此前 PaConvert 验证项仍未看到作者在本 PR 中明确回复;如果对应验证已经完成,请回复确认验证记录,或说明剩余失败与本 PR 无关。
处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。
-
优先级:P3 非行级:PR 描述不在 changed diff line 上。当前描述仍按 plain
paddle.enable_compat()即启用 alias、_nativehelper、以及flash_attention.py显式修改来表述;当前实现实际是level=2才启用 caller-aware alias,compat-only root 符号也通过 torch proxy 的 level=2 专用路径补齐。请把描述同步到当前语义。处理要求:请针对该评论进行回复(同意并已修改请回复 Done,不同意请说明理由)。
|
/re-run all-failed |
1 similar comment
|
/re-run all-failed |
|
Fleet 失败貌似是 CI 问题 |
|
/re-run all-failed |
| # symbol, so ``disable_compat()`` can restore the paddle namespace. | ||
| _PADDLE_NAMESPACE_SAVED: dict[tuple[types.ModuleType, str], Any] = {} | ||
|
|
||
| _COMPAT_ENABLED = False |
There was a problem hiding this comment.
这个flag也是冗余的,直接判断_PADDLE_NAMESPACE_SAVED是否为空就行
| _COMPAT_ENABLED = False | ||
|
|
||
|
|
||
| def is_compat_api_enabled() -> bool: |
There was a problem hiding this comment.
这个接口就不需要了,直接判断len(_PADDLE_NAMESPACE_SAVED)
| _PADDLE_NAMESPACE_SAVED, | ||
| _apply_paddle_namespace_aliases, | ||
| _restore_paddle_namespace_aliases, | ||
| is_compat_api_enabled, |
| for k, v in GLOBAL_OVERRIDES.items() | ||
| if k.startswith(f"{fullname}.") | ||
| } | ||
| if fullname == "torch" and is_compat_api_enabled(): |
There was a problem hiding this comment.
这里改动原因是?对level=1产生了影响吗
There was a problem hiding this comment.
没有对 level=1 产生影响
|
|
||
|
|
||
| @contextmanager | ||
| def use_compat_guard( |
There was a problem hiding this comment.
这个接口不要改动,只针对enable_compat/disable_compat最小范围生效
| paddle.compat.__path__, | ||
| paddle.compat.__name__ + ".", | ||
| ): | ||
| yield importlib.import_module(module_info.name) |
There was a problem hiding this comment.
这里严格判断一下,只针对具备 __all__ 的文件才需要进行迭代dispatch,减少无意义的干扰
| global _COMPAT_ENABLED | ||
|
|
||
| for compat_module in _iter_compat_modules(): | ||
| target_name = compat_module.__name__.replace( |
There was a problem hiding this comment.
如果没有__all__,直接continue,不要浪费时间import_module
|
|
||
| for attr_name in getattr(compat_root, "__all__", ()): | ||
| native_method = getattr(paddle.Tensor, attr_name, None) | ||
| if native_method is None or not callable(native_method): |
There was a problem hiding this comment.
不要判断:not callable(native_method)
后面会将 方法patch为属性、属性patch为方法,无需过度判断
| if current is None or current is compat_attr: | ||
| continue | ||
| _PADDLE_NAMESPACE_SAVED[(target_module, attr_name)] = current | ||
| if isinstance(compat_attr, type): |
There was a problem hiding this comment.
这个分支处理 paddle.compat.nn.Linear 等 class 类的 API
class 除了可调用外,还需要保留继承和 isinstance / issubclass 这样的语义,故不能使用普通函数 dispatcher;这里使用 caller-aware metaclass,在实例化时让外部调用进入 compat class、paddle 内部调用进入 native class,同时保持例如 paddle.nn.Linear 仍然是合法的类型
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前 head。上轮的 root torch.slogdet 暴露问题看起来已经处理完,但这次 use_compat_guard 的恢复语义又回退了,详细说明在行级评论里。
-
优先级:P2 非行级:PaConvert 跨仓验证记录仍未看到明确回复;请补充确认验证结果,或说明剩余失败与本 PR 无关。
-
优先级:P3 非行级:PR 描述仍按“默认
enable_compat()即启用 alias”来写,和当前level=1/2语义不一致;请同步为enable_compat(level=2)才开启paddle.*alias,level=1保持原 proxy 行为。
| """ | ||
| if TORCH_PROXY_FINDER in sys.meta_path: | ||
| sys.meta_path.remove(TORCH_PROXY_FINDER) | ||
| _restore_paddle_namespace_aliases() |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
这里把 _restore_paddle_namespace_aliases() 挂到 disable_compat() 里之后,use_compat_guard() 退出时就不再会把外层的 level=2 namespace alias 恢复回来。enable=False 分支会先 disable_compat() 清空 _PADDLE_NAMESPACE_SAVED,随后只恢复 torch proxy;enable=True 分支也会在 disable_compat() 之后丢掉外层的 paddle.sort / paddle.nn.Linear 等 alias。结果是外层 enable_compat(level=2) 进入 guard 后,退出时会被永久降回 level=1 语义。
建议保留一份“是否需要恢复 alias”的状态,并在 use_compat_guard() 两个 finally 分支里,在恢复 finder 状态后再把 namespace alias 重新装回去:
restore_namespace_aliases = bool(_PADDLE_NAMESPACE_SAVED)
...
disable_compat()
if restore_namespace_aliases:
_apply_paddle_namespace_aliases()
...
enable_compat(scope=None, silent=True)
if restore_namespace_aliases:
_apply_paddle_namespace_aliases()另外,这次把原来覆盖这一行为的 test_use_compat_guard_nested / test_scoped_guard_restores_level2_alias / test_scoped_enable_survives_scoped_module_import 都删掉了,当前没有回归测试兜住这个退化。
There was a problem hiding this comment.
There was a problem hiding this comment.
@SigureMo 这个机器人评论看看对吗,需要改吗,如果use_compat_guard没有level参数,按道理不需要管level=2的情况
There was a problem hiding this comment.
不需要给 use_compat_guard 新增 level 参数。这里的关键不是“透传一个新 level”,而是 use_compat_guard 在进入/退出时要恢复调用前的完整 compat 状态:如果外层已经通过 enable_compat(level=2) 打开了 namespace alias,那么 guard 退出后也必须把这层 alias 重新装回去,否则就会把外层状态永久降回 level=1。所以当前需要修的是 guard 的恢复逻辑,而不是把 level=2 作为新参数往下透传。
There was a problem hiding this comment.
@zhwesky2010 如果两者(enable_compat(level=2) 和 use_compat_guard)没有混合的场景,那确实不需要加,如果保证没有这种使用场景的话,我觉得这里不需要改,否则确实应该加 level 参数
@risemeup1111 请等待确认,如果确认无该使用方法这条 review 就忽略吧
There was a problem hiding this comment.
@risemeup1111 不支持use_compat_guard使用,没有混用的场景,level=2计划仅支持enable_compat中使用
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit;如果最终确认不支持这种混用场景,也需要把相关测试/语义同步收紧。
这条不能按“原 PR 没改到这里所以忽略”处理。当前 use_compat_guard 本身就是公开的可嵌套接口,proxy.py 内部也在用它包住 find_spec / exec_module,而且 test_torch_proxy.py 已经覆盖了 enable=True/False 的嵌套切换。按现有实现,外层 enable_compat(level=2) 进入 use_compat_guard(enable=False) 后,退出时确实会丢掉 namespace alias。
所以结论是:如果项目要支持这种混用,还是需要补恢复逻辑;只有在明确声明“不支持 level=2 与 use_compat_guard 混用”时,这条才能忽略。
There was a problem hiding this comment.
收到。既然这里明确 不支持 use_compat_guard 与 enable_compat(level=2) 混用,那这条关于 guard 退出后恢复 level=2 alias 的问题就可以按不适用处理,当前实现这里不需要再改。
There was a problem hiding this comment.
只有在明确声明“不支持
level=2与use_compat_guard混用”时,这条才能忽略。
已经明确了
There was a problem hiding this comment.
收到,既然已经明确 enable_compat(level=2) 与 use_compat_guard 的混用不在支持范围内,这条关于 guard 退出后恢复 level=2 alias 的 concern 按 superseded 处理,不再作为阻塞项。
|
/re-run all-failed |
|
@SigureMo 整体简化并人工重写了一版,辛苦再review下 |
| return native_cls(*args, **kwargs) | ||
| return super().__call__(*args, **kwargs) | ||
|
|
||
| def __instancecheck__(cls, instance: Any) -> bool: |
There was a problem hiding this comment.
这个建议这么改:给paddle.compat.nn.Linear 加一个metaclass,使其即可以被判断为paddle.nn.Linear
即使没有patch时也支持这样判断,dispatch无需做处理
| for attr_name in getattr(compat_root, "__all__", ()) | ||
| if not hasattr(source_module, attr_name) | ||
| } | ||
| ) |
There was a problem hiding this comment.
这里是针对什么场景?_register_compat_override 会漏掉吗?总感觉在这里加很奇怪啊
| """ | ||
| if TORCH_PROXY_FINDER in sys.meta_path: | ||
| sys.meta_path.remove(TORCH_PROXY_FINDER) | ||
| _restore_paddle_namespace_aliases() |
There was a problem hiding this comment.
@zhwesky2010 如果两者(enable_compat(level=2) 和 use_compat_guard)没有混合的场景,那确实不需要加,如果保证没有这种使用场景的话,我觉得这里不需要改,否则确实应该加 level 参数
@risemeup1111 请等待确认,如果确认无该使用方法这条 review 就忽略吧
risemeup1111
left a comment
There was a problem hiding this comment.
已复查当前提交。前面关于 enable_compat(level=2) 与 use_compat_guard 混用的 concern 现已明确不在支持范围内,可按 superseded 处理;其余已跟进的点也没有新的阻塞项。整体看起来可以合入。
…bclasscheck__ in api_dispatch.py
risemeup1111
left a comment
There was a problem hiding this comment.
已复查新提交。此前 use_compat_guard 与 level=2 混用的 concern 仍按不支持场景处理,不再作为阻塞项;这次新增的 Linear metaclass 调整里发现一个 class alias 类型判定回退,详细见行级评论,需要修复后再合入。
我做了 Python 语法编译和 git diff --check,都通过;完整 Paddle 单测在当前源码目录缺少已安装 wheel / libpaddle,未能本地执行。
| def __call__(cls, *args: Any, **kwargs: Any) -> Any: | ||
| if cls is proxy: | ||
| if ( | ||
| len(_PADDLE_NAMESPACE_SAVED) > 0 | ||
| and not _caller_is_paddle_internal() | ||
| ): | ||
| return compat_cls(*args, **kwargs) | ||
| return native_cls(*args, **kwargs) | ||
| return super().__call__(*args, **kwargs) |
There was a problem hiding this comment.
优先级:P1
处理要求:请针对该评论修复并提交新的 commit。
这里删掉 proxy 自身的 __instancecheck__ / __subclasscheck__ 后,level=2 下的 class alias 会丢掉基本类型判定语义:paddle.nn.Linear 被替换成这个 proxy,但外部调用 paddle.nn.Linear(...) 实际返回的是 compat_cls(...),也就是 paddle.compat.nn.Linear 实例,而不是 proxy 实例。因此 isinstance(paddle.nn.Linear(2, 2), paddle.nn.Linear) 会变成 False,issubclass(paddle.compat.nn.Linear, paddle.nn.Linear) 也会变成 False。这和 PR 描述里“Class APIs retain ... type-checking semantics”的目标不一致,也会影响依赖 isinstance(module, paddle.nn.Linear) 的迁移代码。
可以把类型判定保留在 proxy 这一层,但只对 cls is proxy 生效,避免影响用户从 proxy 派生出来的子类:
| def __call__(cls, *args: Any, **kwargs: Any) -> Any: | |
| if cls is proxy: | |
| if ( | |
| len(_PADDLE_NAMESPACE_SAVED) > 0 | |
| and not _caller_is_paddle_internal() | |
| ): | |
| return compat_cls(*args, **kwargs) | |
| return native_cls(*args, **kwargs) | |
| return super().__call__(*args, **kwargs) | |
| def __call__(cls, *args: Any, **kwargs: Any) -> Any: | |
| if cls is proxy: | |
| if ( | |
| len(_PADDLE_NAMESPACE_SAVED) > 0 | |
| and not _caller_is_paddle_internal() | |
| ): | |
| return compat_cls(*args, **kwargs) | |
| return native_cls(*args, **kwargs) | |
| return super().__call__(*args, **kwargs) | |
| def __instancecheck__(cls, instance: Any) -> bool: | |
| if cls is proxy: | |
| return isinstance(instance, (native_cls, compat_cls)) | |
| return super().__instancecheck__(instance) | |
| def __subclasscheck__(cls, subclass: Any) -> bool: | |
| if cls is proxy: | |
| return issubclass(subclass, native_cls) or issubclass( | |
| subclass, compat_cls | |
| ) | |
| return super().__subclasscheck__(subclass) |
There was a problem hiding this comment.
已在 /python/paddle/compat/utils.py 中使用 metaclass 的方式实现了
|
|
||
|
|
||
| class Linear(nn.Layer): | ||
| _NATIVE_LINEAR = nn.Linear |
There was a problem hiding this comment.
这里不要加这些多余变量
nn/_init_.py里的class都需要做处理
有没什么低成本的写法
There was a problem hiding this comment.
那我的理解就是对所有参与 patch 且存在对应 paddle.nn.* 实现的 paddle.compat.nn 类复用一个通用 metaclass,统一处理 native/compat 的 isinstance 和 issubclass 语义?
There was a problem hiding this comment.
所有的 paddle.compat.* 类 都可以被判定为paddle.*类,无论是否dispatch,都支持这样判断
| yield compat_module | ||
|
|
||
|
|
||
| def _make_caller_aware_class_proxy(native_cls: type, compat_cls: type) -> type: |
There was a problem hiding this comment.
这个还是需要吗,直接用paddle.compat.nn.Linear patch paddle.nn.Linear会有什么问题?
There was a problem hiding this comment.
外部调用场景下,直接用 paddle.compat.nn.Linear patch paddle.nn.Linear 应该是没有问题的,但现在 proxy 类还有让 Paddle 内部调用仍构造 native 类的功能,删掉会影响 Paddle 内部调用,比如像 fleet/layers/mpu/mp_ops.py 第 649 行会传入 weight_attr、bias_attr 等参数:
paddle.nn.Linear(
...,
weight_attr=param_attr,
bias_attr=bias_attr,
name=name,
)
而 compat Linear 没有这些参数;install_check.py 第 109 行也有类似的调用
There was a problem hiding this comment.
@Manfredss
内部调用应该dispatch到native_fn,外部调用才dispatch到compat_fn,是fleet/layers/mpu/mp_ops.py没有触发内部调用的判定吗
There was a problem hiding this comment.
mp_ops._parallel_linear 能触发内部调用判定。当前调用路径是 _parallel_linear -> _CompatAwareMeta.__call__ -> _caller_is_paddle_internal,会返回 native 类。直接用 compat 类覆盖 native 类时失败,是因为直接赋值会绕过 dispatcher
… compat_enhance
|
/re-run all-failed |
PR Category
User Experience
PR Types
Improvements
Description
This PR adds an optional compatibility level to
paddle.enable_compat.level=1remains the default and preserves the existing compatibility behavior.level=2includes alllevel=1behavior and additionally dispatches alignedpaddle.compat.*APIs through their correspondingpaddle.*namespaces. Only public compat APIs declared in__all__participate in this dispatch. Calls from user code use the compat implementation, while Paddle-internal callscontinue to use the original native implementation. Class APIs retain their class, inheritance, and type-checking semantics. Compat APIs without a corresponding
paddle.*API, currentlyslogdet, are exposed only through the Torch proxy inlevel=2.paddle.disable_compat()restores all affected Paddle namespace entries. Therefore, existinglevel=1users and previously passing tests are unaffected. Targeted tests cover namespace dispatch, restoration, class APIs, andslogdet.是否引起精度变化
否