Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / codegen / inline / forward_edits.py: 84%
70 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""Apply source-visible forward extraction patches."""
17from __future__ import annotations
19import textwrap
20from collections.abc import Iterable
22from hyper_parallel.codegen.astkit.edits import TextEdit, replace_function_body
23from hyper_parallel.codegen.astkit.index import FunctionInfo, build_source_index
24from hyper_parallel.codegen.emit.parallel import _build_forward_impl_edit
25from hyper_parallel.codegen.inline.ir import ForwardExtractPatch
27_HIDDEN_ARG_TOKEN = "__hp_hidden_arg__"
30def forward_extract_edits(
31 source_text: str,
32 patches: Iterable[ForwardExtractPatch],
33) -> list[TextEdit]:
34 """Build edits that preserve original forwards and install dispatch bodies."""
35 patch_list = tuple(patches)
36 if not patch_list:
37 return []
38 index = build_source_index(source_text)
39 edits: list[TextEdit] = []
40 for patch in patch_list:
41 cls = index.find_class(patch.class_name)
42 if cls is None:
43 continue
44 func = cls.methods.get(patch.method_name)
45 if func is None:
46 continue
47 body = _bind_forward_args(_bind_hidden_arg(patch.body, func), func)
48 prefix = _parallel_state_prefix(body)
49 if patch.merge_original_body:
50 edits.extend(_merged_forward_edits(source_text, patch, func, body, prefix))
51 continue
52 impl = _build_forward_impl_edit(source_text, func, prefix=prefix)
53 if impl is not None:
54 edits.append(impl)
55 edits.extend(replace_function_body(source_text, func, body, indent=" "))
56 return edits
59def _parallel_state_prefix(body: str) -> str:
60 """Render static enable flags read by a generated dispatch body."""
61 lines = [
62 f" {axis}_enable = False"
63 for axis in ("tp", "cp", "ep")
64 if f"self.{axis}_enable" in body
65 ]
66 return "\n".join(lines) + "\n\n" if lines else ""
69def _merged_forward_edits(
70 source_text: str,
71 patch: ForwardExtractPatch,
72 func: FunctionInfo,
73 body: str,
74 prefix: str,
75) -> list[TextEdit]:
76 """Merge dispatch with the original body when no separate impl is needed."""
77 fallback = _forward_impl_fallback(func)
78 if fallback not in body:
79 raise RuntimeError(
80 f"codegen: merged strategy for {patch.class_name}."
81 f"{patch.method_name} has no original-forward fallback"
82 )
83 dispatch = body.replace(fallback, "", 1).rstrip()
84 original = textwrap.dedent(source_text[func.body_start:func.body_end]).rstrip()
85 edits = list(replace_function_body(
86 source_text, func, f"{dispatch}\n\n{original}", indent=" "
87 ))
88 if prefix:
89 edits.insert(0, TextEdit(func.def_offset, func.def_offset, prefix))
90 return edits
93def _bind_hidden_arg(body: str, func: FunctionInfo) -> str:
94 """Bind a strategy shell's hidden-input placeholder to its host signature."""
95 if _HIDDEN_ARG_TOKEN not in body:
96 return body
97 if not func.param_names:
98 raise RuntimeError(
99 f"codegen: inline strategy body references {_HIDDEN_ARG_TOKEN!r} "
100 "but the target method has no positional parameter to bind it to"
101 )
102 return body.replace(_HIDDEN_ARG_TOKEN, func.param_names[0])
105def _bind_forward_args(body: str, func: FunctionInfo) -> str:
106 """Bind a dispatch shell's argument token to the original signature."""
107 token = "__hp_forward_args__"
108 if token not in body:
109 return body
110 args = list(func.param_names)
111 if func.vararg_name:
112 args.append(f"*{func.vararg_name}")
113 args.extend(f"{name}={name}" for name in func.kwonly_names)
114 if func.kwarg_name:
115 args.append(f"**{func.kwarg_name}")
116 return body.replace(token, ", ".join(args))
119def _forward_impl_fallback(func: FunctionInfo) -> str:
120 """Render the dispatch-shell fallback that calls the extracted method."""
121 args = list(func.param_names)
122 if func.vararg_name:
123 args.append(f"*{func.vararg_name}")
124 args.extend(f"{name}={name}" for name in func.kwonly_names)
125 if func.kwarg_name:
126 args.append(f"**{func.kwarg_name}")
127 return f"return self._forward_impl({', '.join(args)})"
130__all__ = ["forward_extract_edits"]