Python - Kivy: AttributeError: 'super' object has no attribute '__getattr__' when trying to get self.ids
Question: I wrote a code for a kind of android lock thing, whenever I try to get an
specific ClickableImage using the id it raises the following error:

    
    
    AttributeError: 'super' object has no attribute '__getattr__'
    

I've spent hours trying to look for a solution for this problem, I looked
other people with the same issue, and people told them to change the site of
the builder, because it needed to be called first to get the ids attribute or
something like that, but everytime I move the builder, it raises the error
"class not defined". Any clues?

Here is my code:

    
    
    from kivy.app import App
    from kivy.config import Config
    from kivy.lang import Builder
    from kivy.graphics import Line
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.uix.widget import Widget
    from kivy.uix.image import Image
    from kivy.uix.floatlayout import FloatLayout
    from kivy.uix.behaviors import ButtonBehavior
    
    #Variables
    cords = ()
    bld = Builder.load_file('conf.kv')
    
    class Manager(ScreenManager): pass
    class Principal(Screen): pass
    
    class ClickableImage(ButtonBehavior, Image):
        def on_press(self):
            self.source = 'button_press.png'
    
        def on_release(self):
            self.source = 'button.png'
            self.ids.uno.source = 'button_press.png'
    
    class canva(Widget):
        def on_touch_down(self, touch):
            global cords
            with self.canvas:
                touch.ud['line'] = Line(points=(touch.x, touch.y), width=1.5)
            cords = (touch.x, touch.y)
    
        def on_touch_move(self,touch):
            global cords
             touch.ud['line'].points = cords + (touch.x, touch.y)
    
        def on_touch_up(self,touch):
            self.canvas.clear()
    
    class Api(App):    
        def build(self):
            return bld
    
    if __name__ == '__main__':
        Api().run()
    

and here is my .kv file:

    
    
    # conf to file:  test.py
    
    <Manager>:
        Principal:
    
    <Principal>:
        GridLayout:
            size_hint_x: 0.5
            size_hint_y: 0.6
            width: self.minimum_width
            cols: 3
            ClickableImage:
                id: 'uno'
                size: 10,10
                source: 'button.png'
                allow_strech: True
            ClickableImage:
                id: 'dos'
                size: 30,30
                source: 'button.png'
                allow_strech: True
        canva:
    


Answer: Let's look at the output:

    
    
    'super' object has no attribute '__getattr__'
    

In kv language `id` is set in a special way(up to 1.9.2 now), its value is not
a string, because it's not a casual variable. You can't access it with
`<widget>.id`.

I'd say it's similar to `canvas`, which is not a widget, yet it may look like
that(which is why I was confused by your code :P). You've already noticed
`something: <some object>` is like Python's `something = <object>` and that's
(at least what I think) is the whole point of `id`'s value not being a
string(which to some is odd). If `id` was a string, there'd probably be needed
a check to exclude it somehow from casual assigning values. Maybe it's because
of performance or just simplicity.

Therefore let's say `id` is a keyword for a future keyword. In fact, it is,
because the characters assigned to `id` will become a string key with a value
of object got from WeakProxy, to the object WeakProxy points to. Or better
said:

    
    
    id: value
    

becomes

    
    
    <some_root_widget>.ids[str(value)] = weakref.proxy(value)
    

where `value` becomes an _object_(what `print(self)` would return)

I suspect(not sure) that if you use string as the value for `id`, you'll end
up with [weakref](http://stackoverflow.com/a/36789779/5994041) /
[WeakProxy](https://github.com/kivy/kivy/blob/master/kivy/weakproxy.pyx)
pointing to a string. I use the word `point` as it reminds me pointers, don't
get confused with C pointers.

Now if you look again at the output:

  * [super](http://stackoverflow.com/documentation/python/809/compatibility-between-python-3-and-python-2/9712/compatible-subclassing-with-super#t=201608251807170382088) gives you an access to a class you inherit from

  * `print('string id'.__getattr__)` will give you the same error, but `'super'` is substituted with the real value, because well... it doesn't have `__getattr__`

Therefore _if_ you assign a _string_ value to `id`, you'll get into this
situation:

    
    
    <some_root_widget>.ids[str('value')] = weakref.proxy('value')  # + little bit of magic
    

Although `str('value')` isn't necessarily wrong, by default you can't create
weakref.proxy for a string. I'm not sure how Kivy handles this with
WeakProxies, but if you assign a string to `id`, roughly this is what you get.

(Please correct me if I'm wrong)

