[API Compatibility] Change compatibility apis to ChangePrefixMatcher, inject paddle.enable_compat(), fix test environment context pollution -part#895
Conversation
…to patch_compat
… calls hit the alias The default enable_compat() (level=1) does not alias paddle.*, so prefix-converted calls (torch.X -> paddle.X) would run native and reject torch-style kwargs. level=2 turns on the paddle.* alias; caller-aware dispatch keeps paddle internals native. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…to patch_compat
…to patch_compat
…eset import_transformer.py: - Inject paddle.enable_compat(level=2) via a transform() post-pass so it lands after the docstring, __future__ imports and the import block (fixes a pre-existing SyntaxError where injected imports preceded `from __future__`). - Gate on real torch imports (torch_packages), not paddle_package_list, so a torch-free file importing only os/einops/setuptools no longer gains a needless paddle import or the process-global compat switch. tests: - Rename confest.py -> conftest.py so the autouse compat-reset fixture is loaded by pytest (was dead due to the typo). - Dedupe the disable logic into conftest.disable_paddle_compat(), imported by apibase and called before each torch reference exec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…into patch_compat
…ompat Switch Tensor.allclose and Tensor.sort to ChangePrefixMatcher and keep softmin's softmax call using torch arg names (input/dim), since compat paddle APIs now accept torch-style args and reject paddle-native ones. Update tests to inject enable_compat(level=2); fix torchvision model_apibase proxy leak around the torch reference forward.
| # enable_compat is injected in transform() (needs all imports in the body). | ||
| # Gate on real torch imports, not paddle_package_list: the latter also holds | ||
| # MAY_TORCH packages (os/einops/setuptools) that need no compat switch. | ||
| if self.imports_map[self.file]["torch_packages"]: |
There was a problem hiding this comment.
简化下代码,看有无更简单的写法,是否只需要修改visit_Module就可以?
|
PR冲突了 |
|
注意代码风格简化并贴合已有架构来写,不要让AI随意发挥:
|
| return True | ||
| return False | ||
|
|
||
| def _inject_enable_compat(self): |
There was a problem hiding this comment.
这个使用record_scope不能插入吗?插入这个不需要从0开始重写吧
Bring the latest API compatibility coverage into the branch while preserving the record_scope-based compat injection and test state isolation fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for your contribution! |
Align expected diffs with compat injection and intentional ChangePrefixMatcher behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Deduplicate paddle_package_list in ImportTransformer.visit_Module: since imports are now batched into one record_scope call, its cross-call dedup no longer removes duplicates, producing two 'import paddle' lines when a file imports multiple torch-family packages (e.g. torchvision + datasets). Regenerate default-mode goldens for the paddle.enable_compat(level=2) injection and the intentional compat-prefix removals (equal, BatchNorm1d/2d), matching the min-mode baseline update in f7e1c04. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| line_NO += 1 | ||
| import_nodes = [] | ||
| for paddle_package in dict.fromkeys(paddle_package_list): | ||
| import_nodes.extend(ast.parse(f"import {paddle_package}").body) |
There was a problem hiding this comment.
直接在这里判断是否为 paddle_package=='paddle'
如果是,则import_nodes.extend(ast.parse(f"import paddle;paddle.enable_compat(2)").body)
并且该操作仅执行一次(当然执行多次问题也不大,后续会被isort校正)
|
|
||
| has_torch_package = bool(self.imports_map[self.file]["torch_packages"]) | ||
|
|
||
| has_enable_compat = any( |
There was a problem hiding this comment.
这些判断都需要吗?后面也会经过isort、black的格式化处理,移除多余import,这里不要想的太复杂了
| if has_torch_package and not has_enable_compat: | ||
| import_nodes.extend(ast.parse("paddle.enable_compat(level=2)").body) | ||
|
|
||
| import_end = 1 if node.body and ast.get_docstring(node, clean=False) else 0 |
There was a problem hiding this comment.
for循环,self.record_scope((self.root, "body", line_NO), import_nodes) 就行了吧。
原始的node在visit_Import、visit_ImportFrom时就已经被清除,原始node的import内容应该已经被清理掉了。
另外加上log_info,这个是比较重要的调试信息
…a single paddle.enable_compat(level=2) on top of the already-cleaned node, dropping redundant enable_compat/torch-package scans and adding log_info
…into patch_compat
| # Under `paddle.enable_compat(level=2)` the prefix-converted calls | ||
| # (torch.X -> paddle.X) resolve to the torch-aligned paddle.compat.* impls, | ||
| # so inject it once when `import paddle` is added. isort/black dedupe repeats. | ||
| if "paddle" in paddle_package_list: |
There was a problem hiding this comment.
这个应该得 == "paddle" ,然后做个一次性判断 "enable_compat" not in import_code
…hangePrefixMathcer.
| while ( | ||
| import_end < len(node.body) | ||
| and isinstance(node.body[import_end], ast.ImportFrom) | ||
| and node.body[import_end].module == "__future__" |
There was a problem hiding this comment.
这个写得有点hard code,直接修改diff文件吧
|
|
||
| python -m pytest -v -s -p no:warnings "${PYTEST_IGNORE[@]}" \ | ||
| --ignore=tests/test_cuda_stream.py \ | ||
| --ignore=tests/test_cuda_CUDAGraph.py \ |
There was a problem hiding this comment.
这三个单测不跑了吗,建议在这里加个list维护一个隔离的单测名单,分两批来跑
后面这个list还可以根据实际情况进一步扩容
There was a problem hiding this comment.
跑的,分别在 53、59、64 行单独跑了
| # internal `import torch` / `torch.SymInt` etc. resolve through the | ||
| # paddle proxy and blow up. Disable compat for the torch forward, then | ||
| # restore it so the paddle forward matches how a user runs the output. | ||
| from paddle.compat.proxy import TORCH_PROXY_FINDER |
There was a problem hiding this comment.
这个是影响到了paddle内部的paddle.vision.* API吗
There was a problem hiding this comment.
不是 paddle.vision.* API 本身受到影响
转换后的 Paddle 代码执行 paddle.enable_compat(level=2) 后会全局启用 Torch proxy;随后执行真实 torchvision 模型 forward 时,其内部的 Torch 引用可能被代理到 Paddle;因此这里临时关闭 compat,完成 Torch 侧计算后再恢复
| ) | ||
| assert paddle_code == expect_paddle_code, error_msg | ||
| elif compared_tensor_names: | ||
| disable_paddle_compat() |
There was a problem hiding this comment.
APIBase.run() 会先执行真实 torch 代码,再执行转换后的 paddle 代码。paddle 代码中的 paddle.enable_compat(level=2) 执行后,compat 状态会一直保持到这次 run() 结束之后;如果同一个 pytest case 再次调用 run(),那 autouse fixture 这个时候还没有执行清理,第二次运行的 import torch 就会被代理到 paddle,导致 torch 基准结果实际上由 paddle 计算 所以必须在每次执行 torch 代码前关闭 compat。
| except Exception as e: | ||
| raise AssertionError(f"Unable to align results: {e}") | ||
| else: | ||
| disable_paddle_compat() |
| The logic is lazy and best-effort: it only touches a framework that is already in | ||
| ``sys.modules``, so it never forces an import of torch/paddle (which would change | ||
| import ordering) for tests that don't use them. | ||
| """ |
There was a problem hiding this comment.
这个的作用是,看起来是paddle的改动还有些判断不完善吗,是不是应该去改paddle?
尽量是去完善问题的根源,避免在其他地方打补丁,不然就把坑留给用户了
PR Docs
For the 24
torch.*APIs that are aligned throughpaddle.compat.*, switch the converter to the "prefix-only + enable_compat" strategy instead of mapping each topaddle.compat.Xexplicitly:api_mapping.json— change those 24 entries fromChangeAPIMatcher → paddle.compat.Xto{"Matcher": "ChangePrefixMatcher"}. Converted calls now use the correspondingpaddle.*prefix while preserving Torch-style signatures and argument names. For example,torch.sort(x, dim=-1)is converted to:paddle.sort(x, dim=-1). At runtime,enable_compat(level=2)dispatches the externalpaddle.sortcall to its Torch-aligned implementation. Related mappings are adjusted accordingly:torch.nn.Softmax2dusesdiminstead of the native Paddleaxis;torch.nn.functional.softminpreservesinputanddim.transformer/import_transformer.py— for converted files whose mapped packages includepaddle, inject a deduplicatedimport paddleand a singlepaddle.enable_compat(level=2). In default mode, the code is inserted after the existing import block. In min mode, it is inserted after the module docstring and any__future__imports, but before retained Torch imports, ensuring that the compat proxy is enabled before Torch is imported.tests/apibase.py—paddle.enable_compat(level=2)installs process-wide Torch proxy and Paddle namespace dispatch state. Call the shareddisable_paddle_compat()helper immediately before every real PyTorch reference execution, ensuring that the reference always runs against real PyTorch, including when a test invokesrun()multiple times. Cross-test process state is restored separately by the autouse fixture intests/conftest.py.PR APIs
related Paddle PR:
paddle.enable_compat-part Paddle#79391paddle.Tensor.splitis a known failure. It will be fixed inAlso in this PR
The issue of Cross-Test Global State Pollution in
conftest.pyis fixed. Originally PaConvert tests execute the original torch code and converted Paddle code sequentially in the same pytest worker. Some tests modify process-wide state without restoring it, including:paddle.enable_compat(level=2)CPU_NUM;paddle.Tensorandpaddle.nn.*classes.Some generated helpers are defined inside temporary exec namespaces. After those namespaces are cleared, the class-level monkeypatches may remain and reference invalid globals, affecting subsequent tests. Therefore, cleaning up only the compat proxy is insufficient.
This change adds an autouse fixture that snapshots the relevant state before each test and restores it afterward. It disables compat, restores PyTorch/Paddle defaults and environment variables, and reverts modified class attributes. This isolates tests without changing PaConvert’s conversion behavior.
Co-authored by Claude and Codex