Skip to content

Commit 53d90ca

Browse files
committed
add: monkey patch __setattr__ at the module level when using subclass, to ensure submodule assignments are captured
1 parent ab95372 commit 53d90ca

1 file changed

Lines changed: 43 additions & 4 deletions

File tree

‎traincheck/instrumentor/proxy_wrapper/subclass.py‎

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import functools
12
import logging
23
import os
34
import threading
@@ -14,7 +15,9 @@
1415
from .proxy_basics import is_fake_tensor
1516
from .proxy_registry import get_global_registry
1617

17-
# from .utils import print_debug
18+
SUBCLASS_HOOK_KEY = "_tc_setattr_hook"
19+
20+
logger = logging.getLogger(__name__)
1821

1922

2023
def in_dynamo() -> bool:
@@ -126,9 +129,9 @@ def __init__(
126129
current_time = get_timestamp_ns()
127130

128131
self.__dict__["last_update_timestamp"] = current_time
132+
logger.debug(f"[ProxyParameter] Created ProxyParameter: {self.var_name}")
129133
self.register_object()
130134

131-
# print(f"init: {self.var_name}")
132135
if should_dump_trace and not should_disable_proxy_dumping():
133136
if from_call:
134137
phase = "call"
@@ -141,7 +144,7 @@ def __init__(
141144
self.dump_trace(phase=phase, dump_loc="initing")
142145

143146
def __setattr__(self, name, value):
144-
# print(f"paremeter: {self.var_name}, name = {name}, value = {value}")
147+
145148
super().__setattr__(name, value)
146149
self.update_timestamp()
147150
self.register_object()
@@ -175,7 +178,6 @@ def register_object(self):
175178
)
176179

177180
def dump_trace(self, phase, dump_loc):
178-
# print(f"parameter: {self.var_name}, phase = {phase}, dump_loc = {dump_loc}")
179181
# TODO
180182
var_name = self.__dict__["var_name"]
181183
# assert var_name is not None # '' is allowed as a var_name (root object)
@@ -224,6 +226,9 @@ def proxy_parameter(
224226
return
225227
for name, t in list(module.named_parameters(recurse=False)):
226228
var_type = typename(t, is_runtime=True)
229+
logger.debug(
230+
f"[ProxyParameter] Proxying parameter: {parent_name}.{name} of type {var_type}"
231+
)
227232
module._parameters[name] = ProxyParameter(
228233
t,
229234
logdir,
@@ -244,3 +249,37 @@ def proxy_parameter(
244249
from_call,
245250
from_iter,
246251
)
252+
253+
# we need to instrument the __setattr__ of the module to capture parameter updates
254+
def subclass_setattr_hook(self, name, value):
255+
logger.debug(
256+
f"[ProxyParameter] Module __setattr__ called: {parent_name}.{name} = {type(value)}"
257+
)
258+
if isinstance(value, torch.Tensor) or isinstance(value, torch.nn.Module):
259+
proxy_parameter(
260+
value,
261+
logdir,
262+
log_level,
263+
parent_name + "." + name,
264+
should_dump_trace,
265+
from_call,
266+
from_iter,
267+
)
268+
269+
module.__dict__[SUBCLASS_HOOK_KEY] = subclass_setattr_hook
270+
271+
272+
# instrument torch.nn.Module's setattr
273+
orig_setattr = torch.nn.Module.__setattr__
274+
275+
276+
@functools.wraps(orig_setattr)
277+
def wrapped_setattr(self, name, value):
278+
hook = getattr(self, SUBCLASS_HOOK_KEY, None)
279+
if hook is not None:
280+
# If hook returns True, skip the original setattr; otherwise continue.
281+
hook(self, name, value)
282+
return orig_setattr(self, name, value)
283+
284+
285+
torch.nn.Module.__setattr__ = wrapped_setattr

0 commit comments

Comments
 (0)