-
-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deprecate style.copy(applicator); rework style/applicator setting mechanism #224
Conversation
Actually now that I think about it, is there a reason the applicator needs to be set in the copy method? Couldn't the caller just set the applicator on the copy after making it? |
Just for some more context of what's happening when, the tests in def test_set_default_right_textalign_when_rtl():
root = ExampleNode("app", style=Pack(text_direction=RTL))
root.style.reapply()
# Two calls; one caused by text_align, one because text_direction
# implies a change to text alignment.
assert root._impl.set_alignment.mock_calls == [call(RIGHT), call(RIGHT)] This works as expected because class ExampleNode(Node):
def __init__(self, name, style, size=None, children=None):
super().__init__(
style=style, children=children, applicator=TogaApplicator(self)
)
self.name = name
self._impl = Mock() If you move This extra call — which in the case of I still don't 100% understand the intended behavior of all the internals at play here, but the way these tests are written leads me to believe those early calls are not intended behavior, right? If they are supposed to be doing something, then yeah, |
I think the motivation to passing the applicator into the copy call was to ensure that the applicator is in place before the style values are set, so that all the properties being set are applied. This could also be achieved by applying all the values at the time the applicator is assigned - arguably, that would be a better approach, because at present, changing the applicator won't have the same effect as copying a style and passing in the new applicator.
If it makes you feel any better... I'm not sure I understand the intended behavior either... :-) The current implementation definitely works... but it definitely appears that there's some implied behaviour and reliance on specific behaviours in Toga's usage of Travertino. The core requirements are:
Trying to reverse engineer what I was thinking (mumble) years ago, I think I was trying to ensure that the applicator always existed, so you could always set properties on it, and then catch the consequences of an incomplete Node in the applicator. However, in retrospect, I think it might be better to use the existence of the applicator as the signal that a style can be applied, use the act of setting the applicator to apply the "initial" style, and then require that the node is ready to be applied onto at the time the applicator is assigned. Does that make sense?
Agreed that a "canary" like this would be a worthwhile addition - since Toga is the main consumer of Travertino, it would be worth asserting that updates in Toga (or Travertino, for that matter), aren't going to break Toga on the next release. |
So you're proposing that So, looking through the code, this is a sketch of what's currently happening: WidgetSubclass.__init__(style)
|
| Widget.__init__(style=style)
| |
| | Node.__init__(style=style if style else Pack(), applicator=TogaApplicator(self))
| | |
| | | self.applicator = applicator
| | | self.style = style.copy(applicator)
| | | |
| | | | BaseStyle.copy(applicator)
| | | | |
| | | | | dup.applicator = applicator
| | | | | dup.update(**self)
| | | | | # Each property added sends a call to Pack.apply, which fails
| | | | | # with an AttributeError because the widget doesn't have _impl set yet.
| | | | |
| | | | | return dup
| | | | |___
| | | |_______
| | |___________
| |
| | self._impl = None
| | self.factory = get_platform_factory()
| |______
|
| self._impl = self.factory.WidgetSubClass(interface=self)
| |
| | ImplementationWidget.__init__(interface=interface)
| | |
| | | self.interface = interface
| | | self.interface._impl = self
| | | # Unnecessary, since this is already being assigned to self._impl above
| | |
| | | self.create()
| | | self.interface.style.reapply()
| | | # This is where all the styles are actually applied for the first time.
| | |___________
| |_______________
|___________________ That final Now, currently the widget subclass is calling As far as I can see, each widget always calls So I think we're looking at something like this: WidgetSubclass.__init__(style)
|
| Widget.__init__(style=style)
| |
| | Node.__init__(style=style if style else Pack(), applicator=None)
| | |
| | | self.applicator = applicator # which will have been passed in as None
| | | self.style = style
| | | # Style property triggers a copy when set.
| | | |
| | | | BaseStyle.copy()
| | | | | dup.update(**self)
| | | | | # Each call to Pack.apply will see a lack of applicator and do nothing.
| | | | |
| | | | | return dup
| | | | |___
| | | |_______
| | |___________
| |
| | self.factory = get_platform_factory()
| | self._impl = getattr(self.factory, self.however_we_store_implementation_name)(interface=self)
| | |
| | | ImplementationWidget.__init__(interface=interface)
| | | |
| | | | self.interface = interface
| | | | self.create()
| | | |_______
| | |___________
| |
| | self.applicator = TogaApplicator(self)
| | # Applicator property will trigger a reapply when set.
| |_______________
|___________________ I will, of course, have to pay close attention that updates to Travertino are backwards compatible with Toga. One of the first things will be to deprecate the Does all that seem like a reasonable starting point? |
Correct.
I don't think that's necessarily a requirement - it would be possible to accomodate the API that accepts applicator, and just assign applicator after all the properties have been assigned. However, I think it might be worth doing as a "why is this even possible in the first place" API cleanup.
Yes - the summary of current/future state looks right to me, and the broad approach you've outlined looks like it makes sense. |
I've generalized the style and applicator properties such that setting either one triggers a reapply, as long as the other is already set. That way it's up to the consumer to decide what order they want to assign things, as long as the node is ready for application when the second of the two is assigned. I have two questions:
|
Yes. You can even have multiple fragments for the same number and type - For a deprecation, using
Comments inline as part of the review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good. The one major issue is the one you've already flagged; comments inline.
…applicator gets a ref to node, revamped tests
It occurred to me that the bug this fixes for Toga was never in a released version of Travertino. As such, it doesn't seem like it makes sense to mention fixing it in the release notes for the next version, so I've removed that changenote. Let me know if I'm wrong. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of Qs about some late changes.
Running Toga's core tests against this branch generates a whopping 1,035 warnings from early style application. 🤣 |
Just confirming whether you're ready for another review, or if you're expecting more changes. |
Oh! Sorry, yes, I believe this is ready to be looked at again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely in the final stretch. A couple of minor questions/touchups inline.
Also - as noted inline, it occurs to me that this set of changes may also be a fix for beeware/toga#2305. I'm not sure if we should mark this PR as closing that one, or if we wait until we bump Toga to point at a new Travertino release.
changes/224.removal.1.rst
Outdated
@@ -0,0 +1 @@ | |||
Supplying an applicator to BaseStyle.copy() has been deprecated. If you need to manually assign an applicator to a style, do it separately, after the copy. (In normal usage, though, assigning an applicator to a style is handled automatically when styles and/or applicators are assigned to nodes.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure the "normal usage" comment here is correct... or at least, it's open to a misleading intepretation. I think the interesting feature here is more that "when a new style is assigned to a node, any existing applicator on the node will be use for the new style" - which is more of a bug fix (see beeware/toga#2305), and independent of "applicator argument to copy deprecated". My inclination is to drop the "normal usage" comment here, and add a separate bug fix entry for being able to assign a new complete style to a node (which will also inherit any previous node applicator)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, fair enough. I was just thinking in terms of Toga specifically, I think — and a Toga user should probably never encounter this anyway. And thank you for linking that issue! Didn't realize it was already being tracked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the parenthetical, added separate bugfix changenote, and edited that issue into the initial comment for the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, right. Had momentary amnesia about what you said about whether or not to mark this as fixing it. I think it makes sense... Toga only requires Travertino of at least 0.3.0, right? So as soon as this is included in a released version, it's available to current version of Toga. With the caveat that current installations aren't going to be magically auto-updated, of course...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it boils down to a sort of false positive question. Once Travertino 0.3.1 or 0.4.0 or whatever is released with this patch, it is entirely possible to have an installation of Toga 0.4.8 in which the bug is fixed / feature is added. And any new installation of it presumably will, unless the user specifically pins Travertino 0.3.0 as part of the install.
But it won't be guaranteed to be fixed on existing installations, unless the user updates Travertino, or until they update to the next version of Toga, which can require the newer version of Travertino.
I'm still inclined to think this counts, but as someone without much experience in project management conventions, it's not a hill I'd argue on if you disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your analysis - this PR will fix both bugs in Toga... but in a way that causes warnings in Toga. The catch will be how we release this in a way that minimises disruption to users. If existing Toga users are going to get a thousand warnings when they upgrade Travertino, we may need to do a near parallel release to make sure there's a clear way to avoid the warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that sounds like the way to go.
- Finish Added composite property (and some nearby cleanups) #222
- Finish Updated Pack properties to use new Travertino dataclass-based API toga#2443 (which can include
Pack.font
) and Restructure widget initialization toga#2937, but don't merge them yet - Release a new version of Travertino
- Merge the PRs and release a new version of Toga
- ???
- Profit!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's definitely one approach; the downside being that it will be difficult to avoid some users getting the warnings from Travertino.
The other possible approach would be something like:
- Land Restructure widget initialization toga#2937 in a way that it works with both Travertino releases.
- Cut a Toga release
- Complete the work on Added composite property (and some nearby cleanups) #222
- Cut a Travertino release
- Complete the work on Updated Pack properties to use new Travertino dataclass-based API toga#2443
- Cut a second Toga release
That all hinges on (1) being possible; but if it is, this approach would give a little breathing room between the Toga release and the Travertino release, so the chances of people getting stuck in the middle zone is lessened. It also gives a little more space for (3) and (5) to be completed - and ironically, the longer they take to implement, the less the impact of the Travertino change will be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that makes sense! I had assumed # 1 would be ungainly, but I tried it out just now and what do you know, it's totally doable.
Co-authored-by: Russell Keith-Magee <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good - thanks for the fix!
Fixes beeware/toga#2916, fixes beeware/toga#2305
As discussed in beeware/toga#2919, the root cause wasn't in Toga, but in Travertino. Setting the applicator before assigning properties on the newly created duplicate style meant that those assignments were triggering apply, even though no implementation was yet available.
This passes both Travertino's tests as well as Toga's. CI will only verify the former, so double-checking my work on a local copy of the two together probably isn't a bad idea.
Speaking of which, that's something that seems to come up pretty often for me in working on what is primarily a subproject like this. I bet it would be complicated to set up some way to trigger running Toga's tests against a branch here, but it would be an extra bit of insurance... this bug would've been impossible to miss back in March.
PR Checklist: