Empty children at instantiation
Hello, I am encountering a problem with version 47.0. Specifically, in my case, my tests fail because my ListItem has an empty list of children. My ListItem is instantiated as follows:
self.list_item = ListItem(
Label(str(self.line), markup=False),
classes=self.css_class,
id=f"grep-result-{self.inode}",
)
I think this new behavior was introduced with the changes to LazyMount: https://github.com/Textualize/textual/commit/b8fccd494a151719b7d4d0b1e8426ef81ffa8570#diff-517d10c1480e3774a450b0a8095a4ac3a214de831f7ec457ab648f487fc46308L359
If I understand correctly, the children of a widget are no longer directly added to the _nodes of the DOMNode at instantiation but are stored in a temporary object. The children are really added on compose, during the call to self.mount_composed_widgets() in widget.py.
In my specific case, this poses a problem for me, as I want to manipulate a child (Label) of my list to truncate its content dynamically before it is displayed.
I can always find another solution to retrieve my Label without going through the children of the parent ListItem, by creating a custom ListItem, for example. But I wonder if this behavior is intended. I think that in principle, children is a public property, and in my example, one could consider that the Label is a child of its parent from the moment of instantiation. (This is subjective, I admit). And by the way, I'm wondering if there is another solution other than the children property to retrieve my object that I might not have noticed."
Hey Pablo, thanks for opening this issue. Would you mind providing a minimal reproducible example that mimics the issue you're describing in your context as closely as possible?
You are probably right in all of the conclusions you made but without having some concrete code to look at and run we may end up spending a bunch of time going back and forth without even knowing if we're talking about the same thing.
@rodrigogiraoserrao Yes, no problem, will do asap 👍
@rodrigogiraoserrao Here is the MRE: https://github.com/PabloLec/textual_4015_mre/blob/main/textual_4015_mre/init.py
With test runs showing the behavior difference between versions: https://github.com/PabloLec/textual_4015_mre/actions
So, since version 0.47.0, you can't access an element's children before compose. The children parameter turns up empty.
This is just a basic example, so its practicality might not be obvious at first. In my situation, for instance, I've got this asynchronous component that's responsible for creating ListItems with a Label, which can be pretty lengthy, without any restrictions during creation. Then, there's another component that adds these ListItems to a list and chops down the Label text depending on the screen size. And all this happens before compose, before the user can see the result. I'm focusing on the ListItem example because it's what I'm dealing with, but this is actually a behavior that's common to all components with children.
There are two ways of adding children.
You can do this...
yield ListItem(item)
Or this...
with ListItem():
yield item
Both should work in the same way, but the second will have children added after compose. Which is why its not a good idea to rely on the children being available in the constructor.
Widgets that haven't been mounted aren't that useful, and may break if you use their methods, so we want to discourage using them until everything is mounted.
It shouldn't be much of an issue in practice. But you will need to write your tests diffrently. I assume you have seen our guide on testing Textual apps ?