Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-76522/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# PaddlePaddle__Paddle-76522

This directory converts Paddle PR #76522 into a SWE-Paddle community task candidate.

## Source

| Field | Value |
| --- | --- |
| Repo | `PaddlePaddle/Paddle` |
| PR | [76522](https://github.com/PaddlePaddle/Paddle/pull/76522) |
| PR title | `[Compat] Auto register compat module overrides when enable torch proxy` |
| Base commit | `b5efb98a163a2be2505e72266841e64b88254a8a` |
| Gold commit | `20d9626540daf86096cc5bd11c9b84b398ce7138` |
| Merged at | `2025-11-24` |
| Task type | `refactor` / `compatibility_improvement` |
| Resource | CPU |

## Summary

Improve the torch proxy compatibility layer so enabling the proxy automatically exposes public `paddle.compat` overrides through the corresponding `torch` namespace, including overrides located in nested submodules.

## Why This Is A Good SWE-Paddle Candidate

- It comes from a merged Paddle compatibility PR and represents a refactoring-oriented task rather than a conventional bug fix.
- The expected behavior is externally observable through the proxy namespace instead of implementation-specific source structure.
- The task covers both automatic override registration and nested module proxying while preserving existing fallback behavior.
- The production change is Python-only and can be verified deterministically on CPU without building Paddle native extensions.

## Files

- `proposal.md`: candidate proposal for maintainer triage.
- `instruction.md`: self-contained task statement.
- `solution/code.patch`: production-only Gold patch.
- `tests/test.patch`: independent regression tests.
- `tests/test.sh`: minimal target test command.
- `environment/README.md`: reproduction environment notes.
- `README.md`: task overview and verification entrypoint.

## Verification

```bash
bash tests/test.sh
```

Expected behavior: with only `tests/test.patch` applied to the Base commit, the existing proxy fallback test passes while the new compatibility-override tests fail. After applying `solution/code.patch`, all target tests pass.
27 changes: 27 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-76522/environment/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Environment Notes

This candidate is part of the SWE-Paddle community task set.

## Expected Environment

- Repository: `PaddlePaddle/Paddle`
- Base commit: `b5efb98a163a2be2505e72266841e64b88254a8a`
- Resource: CPU
- GPU required: no
- Build path: Paddle source checkout at the base commit. This Python-only task executes the checkout proxy module with controlled Python modules; a Paddle native source build is not required.

## Run Order

1. Check out `PaddlePaddle/Paddle` at the base commit.
2. Apply `tests/test.patch`.
3. Run `bash tests/test.sh`; the target compatibility behavior should fail before the change.
4. Apply `solution/code.patch`.
5. Run `bash tests/test.sh` again; all target tests should pass.

## Minimal Test Command

```bash
bash tests/test.sh
```

The verifier is responsible for deriving stable F2P and P2P node IDs from repeated runs.
25 changes: 25 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-76522/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 完善 Torch Proxy 对兼容接口的覆盖行为

## 详细描述

启用 Paddle 的 Torch Proxy 后,`paddle.compat` 中公开提供的兼容接口应能够通过对应的 `torch` 命名空间直接访问,无需调用方逐一配置。

当前代理对这类兼容接口的处理不完整。特别是当接口位于嵌套模块中时,通过父级模块访问或使用 import 语句导入,可能无法得到预期的兼容实现。

请完善 Torch Proxy 的接口覆盖行为,确保公开的兼容接口在对应命名空间中正确生效,同时保持现有的接口覆盖和默认代理行为不变。

## 验收说明

* 启用 Torch Proxy 后,`paddle.compat` 各子模块公开提供的接口应能够通过对应的 `torch` 命名空间访问
* 未公开的兼容接口不应被暴露到 `torch` 命名空间
* 嵌套模块中的兼容接口通过属性访问时,应返回对应的兼容实现
* 嵌套模块中的兼容接口通过 import 语句导入时,应返回对应的兼容实现
* 已有的接口覆盖行为不得发生变化
* 对于没有兼容实现或已有覆盖的属性,现有的默认代理行为不得发生变化

## 技术要求

* 熟悉 Python import system、模块代理和动态属性访问机制
* 理解 Paddle Torch compatibility layer 的模块映射方式
* 了解嵌套模块的加载、导入和属性解析过程
* 测试应验证运行时可观察行为,不依赖源码文本、局部变量名或特定的内部数据结构
56 changes: 56 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-76522/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Task Proposal: PaddlePaddle__Paddle-76522

## 1. 来源信息

- Instance ID:`PaddlePaddle__Paddle-76522`
- PR 链接:https://github.com/PaddlePaddle/Paddle/pull/76522
- PR 标题:`[Compat] Auto register compat module overrides when enable torch proxy`
- `base_commit`:`b5efb98a163a2be2505e72266841e64b88254a8a`
- Gold commit:`20d9626540daf86096cc5bd11c9b84b398ce7138`
- merged 时间:`2025-11-24`
- 你的身份:熟悉该模块的 contributor
- 后续联系人:TBD

## 2. 问题一句话

完善 Torch Proxy 的兼容接口覆盖机制,使其在启用时自动注册 `paddle.compat` 中公开提供的接口,并确保嵌套模块中的兼容实现能够通过对应的 `torch` 命名空间正确访问。

## 3. 为什么适合作为 SWE-Paddle 样本

- **真实性**:任务来自已合入的 Paddle PR #76522,不是人工构造的需求。
- **类型多样性**:该任务属于 compatibility layer 的功能完善,可补充 SWE-Paddle 中以常规 bug fix 为主的任务分布。
- **可观察性**:兼容接口是否被自动注册、嵌套模块是否返回预期实现,以及未覆盖属性是否保持原有代理行为,都可以通过运行时对象身份稳定验证。
- **边界清楚**:production change 集中在 `python/paddle/compat/proxy.py`,原 PR 中的测试改动可以与 Gold production patch 清晰分离。
- **环境友好性**:核心逻辑涉及 Python 模块导入、属性访问和代理行为,可以在 CPU 环境中直接运行 checkout 源码,无需 GPU 或 Paddle native source build。

## 4. 任务类型和标签

- 任务类型:`feature_enhancement`
- 执行后端:`cpu`
- 设备范围:`cpu_only`
- 模块标签:`[compat, torch_proxy, import_system, module_proxy, api_compatibility]`

## 5. 验证思路

- 目标测试命令:`bash tests/test.sh`
- 目标测试文件:`test/swe_paddle/test_pr76522_torch_proxy_compat_overrides.py`
- 修复前预期:没有兼容接口覆盖的属性应继续按原有逻辑返回 Paddle 模块中的对应对象;自动注册 `paddle.compat` 公开接口以及嵌套模块覆盖相关测试应失败。
- 修复后预期:应用 production Gold patch 后,P2P 与全部 F2P 均应 pass;公开兼容接口能够通过属性访问和 import 语句从对应的 `torch` 命名空间获得。
- P2P 候选:没有兼容实现的属性继续返回原始 Paddle 对象;已有的 Torch Proxy override 行为保持不变。
- F2P 候选:启用 proxy 时自动注册 `paddle.compat` 中公开导出的接口;嵌套模块中的兼容接口能够通过父模块属性访问和直接 import 正确生效;未公开接口不会被注册。

## 6. 环境与资源

- 资源需求:CPU
- Paddle 来源:`PaddlePaddle/Paddle` source checkout at `base_commit`
- 是否能提供 Docker:暂无
- patch 类型:Python-only
- 最小测试命令:`bash tests/test.sh`
- 是否有 oracle 日志:由 SWE-Paddle verifier 结果另行维护

## 7. 风险自查

- 泄露风险:`instruction.md` 只描述兼容接口自动生效、嵌套模块访问和默认代理行为等运行时结果,不透露 Gold patch 中的类设计、注册过程或内部数据结构。
- 环境风险:测试不依赖真实 PyTorch 安装,也不要求历史 Paddle wheel 与 source checkout 完全匹配。
- flaky 风险:测试使用受控的 Python 模块和确定性的对象身份断言,不依赖网络、GPU、多进程或随机行为。
- 拆分风险:兼容接口自动注册和嵌套模块覆盖共同构成 Torch Proxy 兼容行为的完整改进,且集中在同一 production 文件中,适合作为单个任务。
155 changes: 155 additions & 0 deletions swe-paddle/tasks/PaddlePaddle__Paddle-76522/solution/code.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
diff --git a/python/paddle/compat/proxy.py b/python/paddle/compat/proxy.py
index 018528e1691589087e2074e511c6621a44793561..56f1b559da1fd8be6de38e2adef37072a15c39fc 100644
--- a/python/paddle/compat/proxy.py
+++ b/python/paddle/compat/proxy.py
@@ -12,16 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.

+from __future__ import annotations
+
import importlib
import importlib.abc
import importlib.util
import inspect
+import pkgutil
import sys
import types
import warnings
-from collections.abc import Iterable
from contextlib import contextmanager
-from typing import Any
+from functools import cache
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+ from collections.abc import Iterable


def warning_about_fake_interface(name: str):
@@ -48,12 +54,38 @@ def create_fake_function(name):
return fn


+class OverriddenAttribute:
+ def get_value(self):
+ raise NotImplementedError
+
+
+class LazyImportOverriddenAttribute(OverriddenAttribute):
+ def __init__(self, full_name: str):
+ self._full_name = full_name
+
+ def get_value(self):
+ parts = self._full_name.split(".")
+ root_module = importlib.import_module(parts[0])
+ result = root_module
+ for part in parts[1:]:
+ result = getattr(result, part)
+ return result
+
+
+class RawOverriddenAttribute(OverriddenAttribute):
+ def __init__(self, value: Any):
+ self._value = value
+
+ def get_value(self):
+ return self._value
+
+
class ProxyModule(types.ModuleType):
def __init__(
self,
original_module: types.ModuleType,
proxy_name: str,
- overrides: dict[str, Any],
+ overrides: dict[str, OverriddenAttribute],
):
super().__init__(proxy_name)
self._original_module = original_module
@@ -62,11 +94,13 @@ class ProxyModule(types.ModuleType):

def __getattr__(self, name: str) -> Any:
if name in self._overrides:
- return self._overrides[name]
+ return self._overrides[name].get_value()
return getattr(self._original_module, name)


-GLOBAL_OVERRIDES = {}
+GLOBAL_OVERRIDES: dict[str, OverriddenAttribute] = {
+ "torch.relu": LazyImportOverriddenAttribute("paddle.nn.functional.relu"),
+}

TORCH_PROXY_BLOCKED_MODULES = {
"tvm_ffi",
@@ -74,6 +108,42 @@ TORCH_PROXY_BLOCKED_MODULES = {
}


+def _extend_torch_proxy_overrides(
+ overrides: dict[str, OverriddenAttribute],
+) -> None:
+ GLOBAL_OVERRIDES.update(overrides)
+
+
+@cache
+def _register_compat_override():
+ import paddle.compat
+
+ PADDLE_PREFIX = "paddle.compat"
+ TORCH_PREFIX = "torch"
+ PUBLIC_ATTR_DECLARATION = "__all__"
+
+ compat_overrides = {}
+ for module_info in pkgutil.walk_packages(
+ paddle.compat.__path__,
+ paddle.compat.__name__ + ".",
+ ):
+ module = importlib.import_module(module_info.name)
+ if hasattr(module, PUBLIC_ATTR_DECLARATION):
+ public_attrs = getattr(module, PUBLIC_ATTR_DECLARATION)
+ torch_module_name = module_info.name.replace(
+ PADDLE_PREFIX, TORCH_PREFIX, 1
+ )
+ for attr_name in public_attrs:
+ if attr_name.startswith("_"):
+ continue
+ paddle_attr = getattr(module, attr_name)
+ torch_attr_name = f"{torch_module_name}.{attr_name}"
+ compat_overrides[torch_attr_name] = RawOverriddenAttribute(
+ paddle_attr
+ )
+ _extend_torch_proxy_overrides(compat_overrides)
+
+
def _is_specific_module_or_its_submodule(name: str, module: str) -> bool:
return name == module or name.startswith(f"{module}.")

@@ -189,6 +259,18 @@ class TorchProxyMetaFinder:
for k, v in self._source.__dict__.items():
if k in ("__name__", "__package__", "__path__", "__spec__"):
continue
+ if k in overrides:
+ continue
+ if isinstance(v, types.ModuleType):
+ v = ProxyModule(
+ v,
+ f"{self._target_name}.{k}",
+ {
+ kk.removeprefix(f"{k}."): vv
+ for kk, vv in overrides.items()
+ if kk.startswith(f"{k}.")
+ },
+ )
module.__dict__[k] = v

# Use fullname for the spec name and mark as package when appropriate so that
@@ -223,6 +305,7 @@ def enable_torch_proxy() -> None:
>>> import torch # This will import paddle as torch
>>> assert torch.sin is paddle.sin
"""
+ _register_compat_override()
_clear_torch_modules()
sys.meta_path.insert(0, TORCH_PROXY_FINDER)

Loading