diff --git a/src/travertino/node.py b/src/travertino/node.py index 5ab1f32..6b522eb 100644 --- a/src/travertino/node.py +++ b/src/travertino/node.py @@ -1,6 +1,3 @@ -from inspect import signature - - class Node: def __init__(self, style, applicator=None, children=None): # Parent needs to be primed before style is (potentially) applied with @@ -173,43 +170,29 @@ def refresh(self, viewport): self._root.refresh(viewport) else: if self.applicator: + ###################################################################### # 2024-12: Backwards compatibility for Toga <= 0.4.8 ###################################################################### - # (See below) - if self._old_layout_args(): - self.style.layout(self, viewport) - else: + # Accommodate the earlier signature of layout(), which included the node + # as a parameter. + try: self.style.layout(viewport) + + except TypeError as error: + if ( + ".layout() missing 1 required positional argument: 'viewport'" + in str(error) + ): + self.style.layout(self, viewport) + else: + raise ###################################################################### # End backwards compatibility ###################################################################### self.applicator.set_bounds() - ###################################################################### - # 2024-12: Backwards compatibility for Toga <= 0.4.8 - ###################################################################### - - # Accommodate the earlier signature of layout(), which included the node as a - # parameter. This needs to be called on the *instance* -- to have access to the - # style -- but it needs to be cached on the *class*, so all instances have access - # to it. - - def _old_layout_args(self): - try: - return self.__class__._cached_old_layout_args - except AttributeError: - self.__class__._cached_old_layout_args = ( - "node" in signature(self.style.layout).parameters - ) - - return self.__class__._cached_old_layout_args - - ###################################################################### - # End backwards compatibility - ###################################################################### - def _set_root(self, node, root): # Propagate a root node change through a tree. node._root = root diff --git a/tests/test_node.py b/tests/test_node.py index f2686bc..d3af8d0 100644 --- a/tests/test_node.py +++ b/tests/test_node.py @@ -33,6 +33,18 @@ def layout(self, node, viewport): super().layout(viewport) +class TypeErrorStyle(Style): + # Uses the correct signature, but raises an unrelated TypeError in layout + def layout(self, viewport): + raise TypeError("An unrelated TypeError has occurred somewhere in layout()") + + +class OldTypeErrorStyle(Style): + # Just to be extra safe... + def layout(self, node, viewport): + raise TypeError("An unrelated TypeError has occurred somewhere in layout()") + + @prep_style_class class BrokenStyle(BaseStyle): def reapply(self): @@ -136,39 +148,6 @@ def test_create_node(): assert child3.root == new_node -@pytest.mark.parametrize( - "StyleClass, cached_value", - [ - (Style, False), - (OldStyle, True), - ], -) -def test_layout_signature_check(StyleClass, cached_value): - """After the first call to refresh(), node class should cache args version.""" - - class Applicator: - def set_bounds(self): - pass - - class TestNode(Node): - # So we don't change the actual Node class - pass - - # Before refresh() is called, the cached value isn't set. - assert not hasattr(TestNode, "_cached_old_layout_args") - - # Check again, just to be sure nothing has changed from creating an instance. - node = TestNode(style=StyleClass(), applicator=Applicator()) - assert not hasattr(TestNode, "_cached_old_layout_args") - - # Refresh for the first time. - node.refresh(Viewport(width=10, height=20)) - - # After refreshing, which signature to use for layout() should be cached on the - # class, and thus accessible to both instances. - assert TestNode._cached_old_layout_args == cached_value - - @pytest.mark.parametrize("StyleClass", [Style, OldStyle]) def test_refresh(StyleClass): """The layout can be refreshed, and the applicator invoked""" @@ -224,6 +203,19 @@ def __init__(self, style, children=None): assert child3.applicator.tasks == [] +@pytest.mark.parametrize("StyleClass", [TypeErrorStyle, OldTypeErrorStyle]) +def test_type_error_in_layout(StyleClass): + """The shim shouldn't hide unrelated TypeErrors.""" + + class Applicator: + def set_bounds(self): + pass + + node = Node(style=StyleClass(), applicator=Applicator()) + with pytest.raises(TypeError, match=r"unrelated TypeError"): + node.refresh(Viewport(50, 50)) + + def test_add(): """Nodes can be added as children to another node"""