Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
Proposed fix to send_super #108
Related: Issue #107
I'm not sure if my approach is the most elegant.. but at least it has the same semantics as what obj-c does for you at compile-time.
The idea is the caller tells send_super where to start looking, which 100% emulates what the compiler does in obj-c for you.
If they omit the keyword, they get the (possibly incorrect) automatic behavior we had before.
Not sure how elegant this is though. Maybe we can do better. But this is better than nothing IMHO.
Let me know what you think!
Tested on the following code and it works like a charm:
EDIT: Updated this comment to reflect changed API as discussed below (so that someone googling has good usage to look at in front of their eyes rather than non-existant usage).
I think this is the right approach - while I agree that explicitly passing in the superclass isn't very pretty, it's the most straightforward way of implementing this.
I have a couple of comments/questions though:
PS: Quick note - if you use
First, your question:
Sure, I agree we should support that. Although I see advantages to supporting a string too (less typing!). In fact -- I think you'd be ok with that given your __class__ idea. (I figured a string was easier to type, and I noticed 'get_class' isn't exported to the rubicon.objc namespace by default, and I wanted to be minimally intrusive with this change... so that was the rationale for going with the string).
As for the rest of your comment:
I didn't know about __class__. Nice.
Oh man! I love this idea! I was super bothered by the fact that manually typing the classname for superclass= is very very bug prone. This is doubly true if you go around changing the inheritance heirarchy. You have to also remember to change all calls to send_super. I couldn't figure out how else to lexically scope this. Turns out Python has this __class__ keyword which is lexically scoped perfectly for us!
I love this approach that you propose and can see only benefit to it.
It's true, it breaks existing calls. If we issue a friendly message, as you say, all calls can quickly be found and corrected.
I'm all for this approach you propose. It fixes the potential maintainability nightmare of having subtle errors because you changed your object hierarchy but missed the odd "send_super" call.
In ObjC, even though they don't use that term in their docs -- 'super' is lexically scoped. We need a lexically scoped mechanism to get it right. Enter the __class__ pseudo-keyword in Python which is just that. I love it.
So send_super isn't used much eh? Ha -- I use it all the time. But I can see how not everyone is crazy enough to actually inherit from UIKit classes in Python in their iOS project like I do. :) I noticed the Toga framework uses an entirely different pattern to do its magic, for example.
I'll go ahead and change this PR to do what you say (I have to do a bit of digging on how to get from the 'class' to its superclass but I trust ObjC has easy methods to do it -- if not runtime.py having helpers to do it already).
Thanks so much for your very thoughtful guidance on this! I'm really glad this bug is getting fixed!
I also noticed you just made a PR that addresses a potential python warning related to this. Thanks!
Ok, done. Let me know what you think!
Also note that flake8 on my system flaked (heh) on the __class__ built-in. It complained it doesn't exist. When it does -- code runs fine and Python does the right thing. Likely a bug in flake8. Not sure how to handle this.. :/
Question: I noticed this in the code in class ObjCClass:
That is indeed a feature. Rubicon lets you use two different styles for calling methods with arguments. For example, if you want to call the method
The first style is what you're probably used to - the Python method name is the Objective-C method name with all colons replaced with underscores.
The second style tries to mimic Objective-C's method call syntax a little, by placing the method arguments "in between" the parts of the method name. The first method name part becomes the Python method name, the first argument is passed as a positional argument (right after the opening paren), and all further method name parts and arguments become Python keyword arguments. It's a bit lengthy to explain, it's probably easier to understand by looking at the example code above.
When you call a single-argument method and leave off the underscore at the end, you're actually using the second method call style - but because the method only takes a single argument, the call doesn't use any keyword arguments.
Wow.. you just blew my miiiind.
Yeah, I get it. Clever that you support both styles. The second style is more "objective-c" ish. Also kind of more Pythonesque if you think about it. HA! Didn't know that. Will have to read that relevant part of runtime.py to get a sense for how the trickery is done.
Awesome! Thanks for explaining that.
Yep, things have been somewhat busy lately, so I wasn't able to work much on Rubicon for a few weeks - sorry about that.
The reason why this PR couldn't be merged yet is because it requires #109 for the CI build to pass, and that PR couldn't be merged yet because it hasn't been approved yet (because we have required reviews enabled). @freakboy3742 Apologies for pinging you again, I know you're busy with PyCon preparations and such at the moment - but when you have time, could you have a look at #109? Thanks
Well, the CI build passed anyway, go figure
Thank you for the quick response @freakboy3742! That's quite a long trip, hope you'll arrive soon