I'm Convinced That ui Was Deliberately Designed to be Unhackable: the Rant. 😛
I. ProxyTypes is cool (and old)
Today I started using ProxyTypes to "subclass" the uninheritable ui.View classes [1] (e.g.
TableView and Slider), as @mikael showed a while ago. These classes delegate literally everything to the wrapped view.
Take the following example:
>>> from ui2.subclassing.proxies import ObjectProxy
>>> a = ui.View()
>>> b = ObjectProxy(a)
>>> b.background_color
(0.0, 0.0, 0.0, 0.0)
>>> b
<_ui.View object at 0x10cbbf048>
>>> isinstance(b, ui.View)
True
The ObjectProxy makes it nearly impossible to distinguish b from a. The only way they can be told apart is:
>>> type(b)
<class 'ui2.subclassing.proxies.ObjectProxy'>
II. ui is uncool
These classes can't be added as subviews:
>>> ui.View().add_subview(b)
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: Expected a View
To me, it looks like ui is checking issubclass(type(b), ui.View). Now, there's no reason why ui couldn't have used the simpler isinstance method unless it's deliberately trying to prevent my sort of trickery. The thing is, I've read the source of ProxyTypes (I rewrote most of it yesterday) and it forwards everything, far more than should be necessary. I'm assuming that the _ui library does most of its stuff off of the _objc_pointer, which is totally forwarded and should be covered. For god's sake, even ObjCInstance is forwarded through the ObjectProxy:
>>> ObjCInstance(b)
<b'SUIView_PY3': <SUIView_PY3: 0x15a1342a0; frame = (0 0; 100 100); clipsToBounds = YES; layer = <CALayer: 0x159085120>>>
In short, the ObjectProxy is more than robust enough, this should work. Unless @omz is using some kind of weird C type-checking I don't know about, he's going out of his way to prevent this.
III. Almost solutions
These are sorted by my perceived chance of their success.
1. Create a new ObjectProxy subclass which uses multiple inheritance to also inherit from ui.View. The idea of this is that the class wouldn't actually act as a view itself because of the use of __getattribute__(VERY different from __getattr__.) This is "almost" because conflicts with __slots__ mean that I can't create an ObjectProxy subclass which uses multiple inheritance to also inherit from ui.View. I'll see if I can take __slots__ out of ProxyTypes, since I think they're just for performance.
2. Try to write a proxy with a metaclass overriding __subclasscheck__ so that my issubclass(type(b), ui.View) check passes. This idea is "almost" because I haven't tried it yet. I've never used metaclasses before, so I may be totally wrong about what they can do. The way I see it, there's also a good chance that __slots__ will make my iPad catch on fire when I try this.
IV. Question
My question is: why? Am I getting something wrong? Why not allow this convoluted method of subclassing the uninheritable types?
Lastly, I'd love to hear any other ideas for overcoming this besides what I've proposed.
[1] I rewrote ProxyTypes for PEP8 compliance and Python 3 support (As of tomorrow, it will not have been updated in 10 years 😱) The new version is in ui2 and there's a good chance I got the licensing completely wrong.
