Repository: kivy/kivy

Issue:
kwargs ignored if property set via kv
``` python
import kivy
kivy.require('1.8.1')

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button

Builder.load_string('''
<Button>:
    text: 'bad'
''')

class TestApp(App):
    def build(self):
        return Button(text='good')

if __name__ == '__main__':
    TestApp().run()
```
## <bountysource-plugin>

Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/9367446-kwargs-ignored-if-property-set-via-kv?utm_campaign=plugin&utm_content=tracker%2F42681&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F42681&utm_medium=issues&utm_source=github).
</bountysource-plugin>



Discussion:
Also on kivy 1.8.0

Unsure about this one. I guess kv > py instanciation right now. Inverting it might cause other side effect as well, but i guess your approach seems the right one

with kv, a root rule is always applied last (after all the matching rules).  by analogy, the kwargs in an explicit widget instantiation should be considered like the contents of a root rule.  therefore they should be applied last (rather than in `EventDispatcher.__init__`)

while preparing a fairly obvious patch for this issue I discovered there was a not so obvious case:

`ListView.__init__` augments `kwargs` with an `adapter` key before passing it on to super i.e. to `AbstractView` where `adapter` is a property... very amusing... I'll have to tweak that patch

Here is a proposed fix https://github.com/kivy/kivy/pull/3138

I used this to test it (a modification of kived's with a css rule thrown in):

```
import kivy
kivy.require('1.8.1')

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button

Builder.load_string('''
<Button>:
    text: 'bad'

<.foo>:
    color: 1,1,0,1
''')

class TestApp(App):
    def build(self):
        return Button(text='good',cls=("foo",))

if __name__ == '__main__':
    TestApp().run()
```

Thinking more about this, I agree with tito, this is how it should be. Events and properties are set after widget instantiations  through kv and kv > py in this regard. 

So kv properties are set after the initialization, this is normal and expected behavior since a long time. Is there a valid reason to reverse this behavior?

This is how I expect things to be and it is intuitive that things set in kv take precedence over widget initialization(widget is initialized then kv rules/props are set), the other way around will be highly unintuitive.

The result is that one cannot override default values set in kv at instantiation, but has to update the prop after that.

See this case:

``` python
from kivy.app import App
from kivy.factory import Factory


class TestApp(App):
    def build(self):

        spinner = Factory.Spinner(background_normal='image.jpg')
        # prints 'atlas://data/images/defaulttheme/spinner'
        print(spinner.background_normal)

        button = Factory.Button(background_normal='image.jpg')
        # prints 'image.jpg'
        print(button.background_normal)

        return spinner

if __name__ == '__main__':
    TestApp().run()
```

It does not print `image.jpg` for the spinner. I don't find this intuitive, but unexpected and confusing.


Resolving patch:
diff --git a/kivy/lang.py b/kivy/lang.py
--- a/kivy/lang.py
+++ b/kivy/lang.py
@@ -685,6 +685,46 @@ class MyWidget(Button):
     is reached. This means that initially, previous rules could be used to set
     the property.
 
+Order of kwargs and KV rule application
+---------------------------------------
+
+Properties can be initialized in KV as well as in python. For example, in KV:
+
+.. code-block:: kv
+
+<MyRule@Widget>:
+    text: 'Hello'
+    ramp: 45.
+    order: self.x + 10
+
+Then `MyRule()` would initialize all three kivy properties to
+the given KV values. Separately in python, if the properties already exist as
+kivy properties one can do for example `MyRule(line='Bye', side=55)`.
+
+However, what will be the final values of the properties when
+`MyRule(text='Bye', order=55)` is executed? The quick rule is that python
+initialization is stronger than KV initialization only for constant rules.
+
+Specifically, the `kwargs` provided to the python initializer are always
+applied first. So in the above example, `text` is set to
+`'Bye'` and `order` is set to `55`. Then, all the KV rules are applied, except
+those constant rules that overwrite a python initializer provided value.
+
+That is, the KV rules that do not creates bindings such as `text: 'Hello'`
+and `ramp: 45.`, if a value for that property has been provided in python, then
+that rule will not be applied.
+
+So in the `MyRule(text='Bye', order=55)` example, `text` will be `'Bye'`,
+`ramp` will be `45.`, and `order`, which creates a binding, will first be set
+to `55`, but then when KV rules are applied will end up being whatever
+`self.x + 10` is.
+
+.. versionchanged:: 1.9.1
+
+    Before, KV rules always overwrote the python values, now, python values
+    are not overwritten by constant rules.
+
+
 Lang Directives
 ---------------
 
@@ -1638,7 +1678,8 @@ def create_handler(iself, element, key, value, rule, idmap, delayed=False):
     idmap = copy(idmap)
     idmap.update(global_idmap)
     idmap['self'] = iself.proxy_ref
-    handler_append = _handlers[iself.uid][key].append
+    bound_list = _handlers[iself.uid][key]
+    handler_append = bound_list.append
 
     # we need a hash for when delayed, so we don't execute duplicate canvas
     # callbacks from the same handler during a sync op
@@ -1698,7 +1739,7 @@ def create_handler(iself, element, key, value, rule, idmap, delayed=False):
                 handler_append(bound)
 
     try:
-        return eval(value, idmap)
+        return eval(value, idmap), bound_list
     except Exception as e:
         tb = sys.exc_info()[2]
         raise BuilderException(rule.ctx, rule.line,
@@ -1912,8 +1953,13 @@ def template(self, *args, **ctx):
         self._apply_rule(widget, rule, rule, template_ctx=proxy_ctx)
         return widget
 
-    def apply(self, widget):
+    def apply(self, widget, ignored_consts=set()):
         '''Search all the rules that match the widget and apply them.
+
+        `ignored_consts` is a set or list type whose elements are property
+        names for which constant KV rules (i.e. those that don't create
+        bindings) of that widget will not be applied. This allows e.g. skipping
+        constant rules that overwrite a value initialized in python.
         '''
         rules = self.match(widget)
         if __debug__:
@@ -1921,12 +1967,13 @@ def apply(self, widget):
         if not rules:
             return
         for rule in rules:
-            self._apply_rule(widget, rule, rule)
+            self._apply_rule(widget, rule, rule, ignored_consts=ignored_consts)
 
     def _clear_matchcache(self):
         BuilderBase._match_cache = {}
 
-    def _apply_rule(self, widget, rule, rootrule, template_ctx=None):
+    def _apply_rule(self, widget, rule, rootrule, template_ctx=None,
+                    ignored_consts=set()):
         # widget: the current instantiated widget
         # rule: the current rule
         # rootrule: the current root rule (for children of a rule)
@@ -2063,9 +2110,16 @@ def _apply_rule(self, widget, rule, rootrule, template_ctx=None):
                     key = rule.name
                     value = rule.co_value
                     if type(value) is CodeType:
-                        value = create_handler(widget_set, widget_set, key,
-                                               value, rule, rctx['ids'])
-                    setattr(widget_set, key, value)
+                        value, bound = create_handler(
+                            widget_set, widget_set, key, value, rule,
+                            rctx['ids'])
+                        # if there's a rule
+                        if bound or key not in ignored_consts:
+                            setattr(widget_set, key, value)
+                    else:
+                        if key not in ignored_consts:
+                            setattr(widget_set, key, value)
+
         except Exception as e:
             if rule is not None:
                 tb = sys.exc_info()[2]
@@ -2261,7 +2315,7 @@ def _build_canvas(self, canvas, widget, rule, rootrule):
                     key = prule.name
                     value = prule.co_value
                     if type(value) is CodeType:
-                        value = create_handler(
+                        value, _ = create_handler(
                             widget, instr.proxy_ref,
                             key, value, prule, idmap, True)
                     setattr(instr, key, value)
diff --git a/kivy/uix/widget.py b/kivy/uix/widget.py
--- a/kivy/uix/widget.py
+++ b/kivy/uix/widget.py
@@ -317,13 +317,7 @@ def __init__(self, **kwargs):
 
         # Apply all the styles.
         if not no_builder:
-            #current_root = Builder.idmap.get('root')
-            #Builder.idmap['root'] = self
-            Builder.apply(self)
-            #if current_root is not None:
-            #    Builder.idmap['root'] = current_root
-            #else:
-            #    Builder.idmap.pop('root')
+            Builder.apply(self, ignored_consts=self._kwargs_applied_init)
 
         # Bind all the events.
         if on_args:


Test patch:
diff --git a/kivy/tests/test_lang.py b/kivy/tests/test_lang.py
--- a/kivy/tests/test_lang.py
+++ b/kivy/tests/test_lang.py
@@ -226,3 +226,27 @@ def test_with_two_spaces(self):
         self.assertTrue('on_press' in wid.binded_func)
         wid.binded_func['on_press']()
         self.assertEquals(wid.a, 1)
+
+    def test_kv_python_init(self):
+        from kivy.lang import Builder
+        from kivy.uix.widget import Widget
+
+        class MyObject(object):
+            value = 55
+
+        class MyWidget(Widget):
+            cheese = MyObject()
+
+        Builder.load_string('''
+<MyWidget>:
+    x: 55
+    y: self.width + 10
+    height: self.cheese.value
+    width: 44
+''')
+
+        w = MyWidget(x=22, height=12, y=999)
+        self.assertEqual(w.x, 22)
+        self.assertEqual(w.width, 44)
+        self.assertEqual(w.y, 44 + 10)
+        self.assertEqual(w.height, 12)