Skip to content

Commit abb3c47

Browse files
authored
[BugFix] Add TensorDictModuleBase repr (#1767)
1 parent 8e4daaa commit abb3c47

2 files changed

Lines changed: 45 additions & 13 deletions

File tree

tensordict/nn/common.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -793,8 +793,17 @@ def __name__(self):
793793
# This is necessary to make compiled vmap over TDModule happy
794794
return self.__class__.__name__
795795

796-
def __repr__(self):
797-
return f"{self.__class__.__name__}()"
796+
def __repr__(self) -> str:
797+
entries = [f"{name}={module}" for name, module in self.named_children()]
798+
# a wrapped callable that is not an nn.Module (a plain function, a lambda, ...)
799+
# is stored as a regular attribute, hence missing from the children
800+
module = self.__dict__.get("module")
801+
if module is not None:
802+
entries.insert(0, f"module={module}")
803+
entries.append(f"in_keys={self.in_keys}")
804+
entries.append(f"out_keys={self.out_keys}")
805+
fields = indent(",\n".join(entries), 4 * " ")
806+
return f"{type(self).__name__}(\n{fields})"
798807

799808

800809
class TensorDictModule(TensorDictModuleBase):
@@ -1225,17 +1234,6 @@ def device(self) -> torch.device:
12251234
return p.device
12261235
return torch.device("cpu")
12271236

1228-
def __repr__(self) -> str:
1229-
fields = indent(
1230-
f"module={self.module},\n"
1231-
f"device={self.device},\n"
1232-
f"in_keys={self.in_keys},\n"
1233-
f"out_keys={self.out_keys}",
1234-
4 * " ",
1235-
)
1236-
1237-
return f"{type(self).__name__}(\n{fields})"
1238-
12391237
def __getattr__(self, name: str) -> Any:
12401238
if not is_compiling():
12411239
__dict__ = self.__dict__

test/nn/test_nn.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,40 @@ def my_func(self, tensor: torch.Tensor, *, an_integer: int):
360360

361361
assert td["b"] == 4
362362

363+
def test_repr(self):
364+
class MyModule(TensorDictModuleBase):
365+
in_keys = ["a"]
366+
out_keys = ["b"]
367+
368+
def __init__(self):
369+
super().__init__()
370+
self.net = nn.Linear(3, 4)
371+
372+
def forward(self, tensordict):
373+
tensordict.set("b", self.net(tensordict.get("a")))
374+
return tensordict
375+
376+
string = repr(MyModule())
377+
assert "MyModule(" in string
378+
assert "net=Linear(in_features=3, out_features=4" in string
379+
assert "device=" not in string
380+
assert "in_keys=['a']" in string
381+
assert "out_keys=['b']" in string
382+
383+
# an nn.Module payload is a child: listed once, and not through __dict__
384+
mod = TensorDictModule(nn.Linear(3, 4), in_keys=["a"], out_keys=["b"])
385+
assert "module" not in mod.__dict__
386+
string = repr(mod)
387+
assert string.count("module=") == 1
388+
assert "module=Linear(in_features=3, out_features=4" in string
389+
390+
# a callable that is not an nn.Module is not a child: it comes from __dict__
391+
mod = TensorDictModule(lambda x: x + 1, in_keys=["a"], out_keys=["b"])
392+
assert dict(mod.named_children()) == {}
393+
string = repr(mod)
394+
assert string.count("module=") == 1
395+
assert "module=<function" in string
396+
363397
def test_mutable_sequence(self):
364398
in_keys = self.MyMutableSequence(["a", "b", "c"])
365399
out_keys = self.MyMutableSequence(["d", "e", "f"])

0 commit comments

Comments
 (0)