diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/README.md new file mode 100644 index 000000000..cb2f9a1b3 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/README.md @@ -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. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/environment/README.md b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/environment/README.md new file mode 100644 index 000000000..85142130e --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/environment/README.md @@ -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. diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/instruction.md b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/instruction.md new file mode 100644 index 000000000..560a44a73 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/instruction.md @@ -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 的模块映射方式 +* 了解嵌套模块的加载、导入和属性解析过程 +* 测试应验证运行时可观察行为,不依赖源码文本、局部变量名或特定的内部数据结构 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/proposal.md b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/proposal.md new file mode 100644 index 000000000..0249b6d37 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/proposal.md @@ -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 文件中,适合作为单个任务。 diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/solution/code.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/solution/code.patch new file mode 100644 index 000000000..c5f3062b3 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/solution/code.patch @@ -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) + diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.patch b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.patch new file mode 100644 index 000000000..8d0b8294d --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.patch @@ -0,0 +1,136 @@ +diff --git a/test/swe_paddle/test_pr76522_torch_proxy_compat_overrides.py b/test/swe_paddle/test_pr76522_torch_proxy_compat_overrides.py +new file mode 100644 +index 0000000000000000000000000000000000000000..768c137ea71136d1ed4cc2c39839efe5da9fd1e6 +--- /dev/null ++++ b/test/swe_paddle/test_pr76522_torch_proxy_compat_overrides.py +@@ -0,0 +1,130 @@ ++from __future__ import annotations ++ ++import sys ++import types ++from pathlib import Path ++ ++import pytest ++ ++ ++TARGET = Path("python/paddle/compat/proxy.py") ++ ++ ++def _load_checkout_namespace(): ++ namespace = {"__name__": "checkout_proxy", "__file__": str(TARGET)} ++ source = TARGET.read_text(encoding="utf-8") ++ exec(compile(source, str(TARGET), "exec"), namespace) ++ return namespace ++ ++ ++def _install_fake_compat_modules(monkeypatch): ++ paddle = types.ModuleType("paddle") ++ paddle.__path__ = [] ++ ++ compat = types.ModuleType("paddle.compat") ++ compat.__path__ = ["compat"] ++ compat.__package__ = "paddle" ++ ++ compat_nn = types.ModuleType("paddle.compat.nn") ++ compat_nn.__all__ = ["Linear", "_private"] ++ compat_linear = object() ++ compat_nn.Linear = compat_linear ++ compat_nn._private = object() ++ ++ compat_functional = types.ModuleType("paddle.compat.nn.functional") ++ compat_functional.__all__ = ["softmax"] ++ compat_softmax = object() ++ compat_functional.softmax = compat_softmax ++ ++ compat.nn = compat_nn ++ paddle.compat = compat ++ ++ monkeypatch.setitem(sys.modules, "paddle", paddle) ++ monkeypatch.setitem(sys.modules, "paddle.compat", compat) ++ monkeypatch.setitem(sys.modules, "paddle.compat.nn", compat_nn) ++ monkeypatch.setitem( ++ sys.modules, "paddle.compat.nn.functional", compat_functional ++ ) ++ ++ return compat_linear, compat_softmax ++ ++ ++def test_existing_proxy_module_falls_back_to_original_attribute(): ++ namespace = _load_checkout_namespace() ++ original = types.ModuleType("paddle.fake") ++ marker = object() ++ original.answer = marker ++ ++ proxied = namespace["ProxyModule"](original, "torch.fake", {}) ++ ++ assert proxied.answer is marker ++ ++ ++def test_enable_torch_proxy_auto_registers_public_compat_exports(monkeypatch): ++ namespace = _load_checkout_namespace() ++ compat_linear, compat_softmax = _install_fake_compat_modules(monkeypatch) ++ ++ module_infos = [ ++ types.SimpleNamespace(name="paddle.compat.nn"), ++ types.SimpleNamespace(name="paddle.compat.nn.functional"), ++ ] ++ if "pkgutil" in namespace: ++ monkeypatch.setattr( ++ namespace["pkgutil"], ++ "walk_packages", ++ lambda *args, **kwargs: module_infos, ++ ) ++ ++ namespace["_clear_torch_modules"] = lambda: None ++ fake_sys = types.SimpleNamespace(meta_path=[], modules={}) ++ namespace["sys"] = fake_sys ++ ++ namespace["enable_torch_proxy"]() ++ ++ linear_override = namespace["GLOBAL_OVERRIDES"]["torch.nn.Linear"] ++ softmax_override = namespace["GLOBAL_OVERRIDES"][ ++ "torch.nn.functional.softmax" ++ ] ++ ++ assert linear_override.get_value() is compat_linear ++ assert softmax_override.get_value() is compat_softmax ++ assert namespace["TORCH_PROXY_FINDER"] in fake_sys.meta_path ++ assert "torch.nn._private" not in namespace["GLOBAL_OVERRIDES"] ++ ++ ++def test_nested_override_is_visible_through_parent_proxy(monkeypatch): ++ namespace = _load_checkout_namespace() ++ ++ source_nn = types.ModuleType("paddle.nn") ++ source_nn.__path__ = ["nn"] ++ source_functional = types.ModuleType("paddle.nn.functional") ++ original_softmax = object() ++ source_functional.softmax = original_softmax ++ source_nn.functional = source_functional ++ ++ override_softmax = object() ++ ++ class OverrideValue: ++ def get_value(self): ++ return override_softmax ++ ++ namespace["GLOBAL_OVERRIDES"] = { ++ "torch.nn.functional.softmax": OverrideValue(), ++ } ++ ++ real_import_module = namespace["importlib"].import_module ++ ++ def fake_import_module(name, package=None): ++ if name == "paddle.nn": ++ return source_nn ++ return real_import_module(name, package) ++ ++ monkeypatch.setattr(namespace["importlib"], "import_module", fake_import_module) ++ ++ finder = namespace["TorchProxyMetaFinder"]() ++ spec = finder._find_spec_for_torch_module("torch.nn") ++ proxied_nn = spec.loader.create_module(spec) ++ spec.loader.exec_module(proxied_nn) ++ ++ assert proxied_nn.functional.softmax is override_softmax ++ assert source_functional.softmax is original_softmax diff --git a/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.sh b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.sh new file mode 100644 index 000000000..4d1cd33a2 --- /dev/null +++ b/swe-paddle/tasks/PaddlePaddle__Paddle-76522/tests/test.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +python -m pytest test/swe_paddle/test_pr76522_torch_proxy_compat_overrides.py -q