Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
321f32b
change for enable_compat
Manfredss Jun 29, 2026
9bf7bfb
add two left api
Manfredss Jun 29, 2026
204c7a1
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 3, 2026
edb4042
[API Compatibility] inject enable_compat(level=2) so prefix-converted…
Manfredss Jul 3, 2026
1ff4284
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 7, 2026
020ce44
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 7, 2026
e584efa
[API Compatibility] refine enable_compat injection; activate compat r…
Manfredss Jul 7, 2026
bfff52a
Merge branch 'patch_compat' of https://github.com/manfredss/paconvert…
Manfredss Jul 7, 2026
3dcd2a1
update ChangePrefixMatcher for those can passed under enable_compat(l…
Manfredss Jul 8, 2026
81a3c07
ChangePrefixMatcher for BatchNorm1d/2d/3d
Manfredss Jul 8, 2026
8170d4f
[API Compatibility] fix softmin/allclose/sort matchers under enable_c…
Manfredss Jul 9, 2026
f04bbf3
fix code conversion mis-alignment
Manfredss Jul 9, 2026
9bef7f9
Merge upstream master into patch_compat
Manfredss Jul 14, 2026
838a8a5
restore test script
Manfredss Jul 14, 2026
f7e1c04
update min-mode consistency baselines
Manfredss Jul 14, 2026
5fe7de1
fix default-mode consistency: dedupe injected imports, update baselines
Manfredss Jul 14, 2026
14d8aeb
Simplify ImportTransformer.visit_Module to insert paddle imports and …
Manfredss Jul 15, 2026
e0808c4
Merge branch 'patch_compat' of https://github.com/manfredss/paconvert…
Manfredss Jul 15, 2026
6bd002d
paddle_package == paddle and add once
Manfredss Jul 16, 2026
f8f344a
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 17, 2026
961af0c
update after paddle changed
Manfredss Jul 20, 2026
d8f6e9d
fix
Manfredss Jul 22, 2026
62739d7
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 22, 2026
7bb47d1
improve
Manfredss Jul 22, 2026
67be297
AvgPool1/2/3d, BatchNorm1/2/3d, MultiheadAttention, Tensor.split to C…
Manfredss Jul 24, 2026
1f774a4
Merge branch 'master' of https://github.com/paddlepaddle/paconvert in…
Manfredss Jul 27, 2026
9c52c23
fix
Manfredss Jul 28, 2026
e39b042
add fix for split, to help distinguish from string split() and Tensor…
Manfredss Jul 28, 2026
8ee2f99
fix
Manfredss Jul 30, 2026
22d59d6
delete same conversion param for nn.functional.softmin
Manfredss Jul 30, 2026
7f11a54
enhance three Tensor api tests, enhance Categorical tests, and PReLU …
Manfredss Jul 30, 2026
5362b97
still fix split typo
Manfredss Jul 30, 2026
1f99f24
clean
Manfredss Jul 31, 2026
d9a9a74
split root cause
Manfredss Jul 31, 2026
5cd351e
fix
Manfredss Aug 7, 2026
770552a
add reason for check_value=False
Manfredss Aug 7, 2026
38925e2
fix backslash
Manfredss Aug 7, 2026
9091b39
use paddle.compat.split rather than paddle.tensor.split, since enable…
Manfredss Aug 10, 2026
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
220 changes: 34 additions & 186 deletions paconvert/api_mapping.json

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions paconvert/transformer/import_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def __init__(
self.imports_map[self.file]["api_alias_name_map"] = {}
self.insert_pass_node = set()
self.change_prefix_api_map = defaultdict(set)
# Set in visit_Module: True when this file actually imports torch (so we
# added paddle imports) and therefore needs paddle.enable_compat injected.
self.need_enable_compat = False

def visit_Import(self, node):
"""
Expand Down Expand Up @@ -525,3 +528,94 @@ def visit_Module(self, node):
(self.root, "body", 0), ast.parse(f"import {paddle_package}").body
)
line_NO += 1

# 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"]:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

简化下代码,看有无更简单的写法,是否只需要修改visit_Module就可以?

self.need_enable_compat = True

def transform(self):
super(ImportTransformer, self).transform()
self._inject_enable_compat()

@staticmethod
def _is_future_import(node):
return isinstance(node, ast.ImportFrom) and node.module == "__future__"

@staticmethod
def _is_import(node):
return isinstance(node, (ast.Import, ast.ImportFrom))

@staticmethod
def _is_docstring(node):
return (
isinstance(node, ast.Expr)
and isinstance(node.value, ast.Constant)
and isinstance(node.value.value, str)
)

@staticmethod
def _is_enable_compat_call(node):
return (
isinstance(node, ast.Expr)
and isinstance(node.value, ast.Call)
and isinstance(node.value.func, ast.Attribute)
and node.value.func.attr == "enable_compat"
and isinstance(node.value.func.value, ast.Name)
and node.value.func.value.id == "paddle"
)

@staticmethod
def _binds_paddle(node):
# `import paddle` or `import paddle.xxx` (no asname) binds the name `paddle`
if isinstance(node, ast.Import):
for alias_node in node.names:
if alias_node.asname is None and (
alias_node.name == "paddle" or alias_node.name.startswith("paddle.")
):
return True
return False

def _inject_enable_compat(self):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个使用record_scope不能插入吗?插入这个不需要从0开始重写吧

"""Insert ``paddle.enable_compat(level=2)`` after the docstring,
``__future__`` imports and the import block (level=2 aliases the
torch-aligned ``paddle.compat.*`` APIs onto ``paddle.*``). Post-pass so it
is never placed above a ``__future__`` import (which is a SyntaxError).
"""
if not self.need_enable_compat:
return

body = [n for n in self.root.body if not self._is_enable_compat_call(n)]

# hoist all __future__ imports (they must precede every other statement)
futures = [n for n in body if self._is_future_import(n)]
body = [n for n in body if not self._is_future_import(n)]

# hoist the module docstring if only imports precede it (we prepend imports)
doc = []
for i, node in enumerate(body):
if self._is_docstring(node) and all(self._is_import(n) for n in body[:i]):
doc = [node]
body = body[:i] + body[i + 1 :]
break

# split off the contiguous import block at the top of the remainder
end = 0
while end < len(body) and self._is_import(body[end]):
end += 1
imports, rest = body[:end], body[end:]

# enable_compat needs the name `paddle` bound (submodule-only aliases may
# not bind it, e.g. `import torch.nn as nn` -> `import paddle.nn as nn`)
if not any(self._binds_paddle(n) for n in imports):
imports = ast.parse("import paddle").body + imports

compat = ast.parse("paddle.enable_compat(level=2)").body
self.root.body = doc + futures + imports + compat + rest
ast.fix_missing_locations(self.root)
log_info(
self.logger,
"add 'paddle.enable_compat(level=2)' after imports",
self.file_name,
)
5 changes: 5 additions & 0 deletions tests/apibase.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import numpy as np

sys.path.append(os.path.dirname(__file__) + "/..")
sys.path.append(os.path.dirname(__file__))

from conftest import disable_paddle_compat

from paconvert.converter import Converter

Expand Down Expand Up @@ -85,6 +88,7 @@ def run(
)
assert paddle_code == expect_paddle_code, error_msg
elif compared_tensor_names:
disable_paddle_compat()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为何需要disable

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APIBase.run() 会先执行真实 torch 代码,再执行转换后的 paddle 代码。paddle 代码中的 paddle.enable_compat(level=2) 执行后,compat 状态会一直保持到这次 run() 结束之后;如果同一个 pytest case 再次调用 run(),那 autouse fixture 这个时候还没有执行清理,第二次运行的 import torch 就会被代理到 paddle,导致 torch 基准结果实际上由 paddle 计算 所以必须在每次执行 torch 代码前关闭 compat

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你这里是不是应该从eval执行的地方去修改,适配好torch、paddle交替执行的情况

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

你这里应该从eval执行的地方去修改,适配torch、paddle交替执行的情况

pytorch_ns = {}
try:
exec(pytorch_code, pytorch_ns)
Expand Down Expand Up @@ -117,6 +121,7 @@ def run(
except Exception as e:
raise AssertionError(f"Unable to align results: {e}")
else:
disable_paddle_compat()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为何需要disable

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

pytorch_ns = {}
try:
exec(pytorch_code, pytorch_ns)
Expand Down
50 changes: 50 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个文件还是不能删除吗

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我目前还没有办法删去

#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys

import pytest


def disable_paddle_compat():
"""Turn OFF Paddle's torch-compat proxy if it is currently active.

Converted Paddle code injects ``paddle.enable_compat(level=2)``, which flips
process-global state: it installs an ``import torch`` -> Paddle proxy and
aliases ``paddle.*`` to the torch-aligned ``paddle.compat.*`` APIs. That state
must be cleared so it cannot leak into (a) a later test, or (b) a torch
*reference* run within the same test, whose ``import torch`` would otherwise be
proxied to Paddle so the reference would no longer be real torch.

Lazy and best-effort: a no-op when Paddle was never imported, so it does not
force a Paddle import (and thus does not change torch/paddle import ordering)
for tests that never touch Paddle.
"""
if "paddle" not in sys.modules:
return
paddle = sys.modules["paddle"]
try:
from paddle.compat.proxy import TORCH_PROXY_FINDER

while TORCH_PROXY_FINDER in sys.meta_path:
paddle.disable_compat()
except Exception:
pass


@pytest.fixture(autouse=True)
def _reset_paddle_compat_mode():
"""Disable torch-compat after every test so it cannot leak into a later one."""
yield
disable_paddle_compat()
48 changes: 25 additions & 23 deletions tests/test_Tensor_median.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,7 @@ def test_case_2():
result = input.median(1)
"""
)
obj.run(
pytorch_code,
["result"],
unsupport=True,
reason="paddle does not return index when dim is specified",
)
obj.run(pytorch_code, ["result"])


def test_case_3():
Expand All @@ -54,12 +49,7 @@ def test_case_3():
result = input.median(1, keepdim=True)
"""
)
obj.run(
pytorch_code,
["result"],
unsupport=True,
reason="paddle does not return index when dim is specified",
)
obj.run(pytorch_code, ["result"])


def test_case_4():
Expand All @@ -70,12 +60,7 @@ def test_case_4():
result = input.median(dim=1, keepdim=True)
"""
)
obj.run(
pytorch_code,
["result"],
unsupport=True,
reason="paddle does not return index when dim is specified",
)
obj.run(pytorch_code, ["result"])


def test_case_5():
Expand All @@ -86,9 +71,26 @@ def test_case_5():
result = input.median(0)
"""
)
obj.run(
pytorch_code,
["result"],
unsupport=True,
reason="paddle does not return index when dim is specified",
obj.run(pytorch_code, ["result"])


def test_case_6():
pytorch_code = textwrap.dedent(
"""
import torch
input = torch.tensor([1.0, 2.0, 3.0, 4.0])
result = input.median()
"""
)
obj.run(pytorch_code, ["result"])


def test_case_7():
pytorch_code = textwrap.dedent(
"""
import torch
input = torch.tensor([[1.0, 2.0, 3.0, 4.0], [8.0, 7.0, 6.0, 5.0]])
result = input.median(dim=1)
"""
)
obj.run(pytorch_code, ["result"])
8 changes: 7 additions & 1 deletion tests/test_Tensor_rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ def test_case_1():
)
obj.run(
pytorch_code,
expect_paddle_code='import paddle\n\nx = paddle.tensor([1, 2, 3])\nx.rename(columns={"iids": iids})\n',
expect_paddle_code="""
import paddle

paddle.enable_compat(level=2)
x = paddle.tensor([1, 2, 3])
x.rename(columns={"iids": iids})
""",
)


Expand Down
1 change: 1 addition & 0 deletions tests/test_Tensor_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ def test_case_7():
"""
import paddle

paddle.enable_compat(level=2)
a = paddle.tensor([1, 2, 3])
str1 = "1,2,3"
str1.split(",")
Expand Down
2 changes: 2 additions & 0 deletions tests/test_add_start_docstrings_to_model_forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def forward(self, input_ids):
import paddle
import paddleformers

paddle.enable_compat(level=2)


class LlamaForCausalLM(paddle.nn.Module):
@paddleformers.trainer.utils.add_start_docstrings_to_model_forward("test docstring")
Expand Down
2 changes: 2 additions & 0 deletions tests/test_jit_ignore.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def helper_function(self, x):
"""
import paddle

paddle.enable_compat(level=2)


class MyModule(paddle.nn.Module):
def forward(self, x):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_jit_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def test_case_1():
"""
import paddle

paddle.enable_compat(level=2)
result = paddle.jit.load(path="model.pt")
"""
)
Expand All @@ -48,6 +49,7 @@ def test_case_2():
"""
import paddle

paddle.enable_compat(level=2)
result = paddle.jit.load(path="model.pt")
"""
)
Expand Down
8 changes: 8 additions & 0 deletions tests/test_jit_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def forward(self, x):
"""
import paddle

paddle.enable_compat(level=2)


class MyModule(paddle.nn.Module):
def __init__(self):
Expand Down Expand Up @@ -89,6 +91,8 @@ def forward(self, x):
"""
import paddle

paddle.enable_compat(level=2)


class MyModule(paddle.nn.Module):
def __init__(self):
Expand Down Expand Up @@ -134,6 +138,8 @@ def forward(self, x):
"""
import paddle

paddle.enable_compat(level=2)


class MyModule(paddle.nn.Module):
def __init__(self):
Expand Down Expand Up @@ -180,6 +186,8 @@ def forward(self, x):
"""
import paddle

paddle.enable_compat(level=2)


class MyModule(paddle.nn.Module):
def __init__(self):
Expand Down
6 changes: 6 additions & 0 deletions tests/test_jit_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def foo(x, scale, shift):
"""
import paddle

paddle.enable_compat(level=2)


@paddle.jit.to_static
def foo(x, scale, shift):
Expand Down Expand Up @@ -66,6 +68,8 @@ def add(x, y):
"""
import paddle

paddle.enable_compat(level=2)


def add(x, y):
return x + y
Expand Down Expand Up @@ -99,6 +103,8 @@ def add(x, y):
"""
import paddle

paddle.enable_compat(level=2)


def add(x, y):
return x + y
Expand Down
6 changes: 4 additions & 2 deletions tests/test_onnx_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,14 @@ def onnx_export(model,f):
############################## 相关utils函数,如上 ##############################


paddle.enable_compat(level=2)


class SimpleModel(paddle.nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = paddle.compat.nn.Linear(3, 3)
self.fc2 = paddle.compat.nn.Linear(3, 1)
self.fc1 = paddle.nn.Linear(3, 3)
self.fc2 = paddle.nn.Linear(3, 1)

def forward(self, x):
x = paddle.relu(self.fc1(x))
Expand Down
Loading