Repository: kivy/kivy

Issue:
kv allows conflicting class overrides
Can be very problematic source of bugs.

In kv:
MyClass@RelativeLayout

In Python
MyClass(Widget):
    pass

Instances of MyClass will behave as a widget, not a relative layout.  Is there any way to detect and prevent this?

kv allows conflicting class overrides
Can be very problematic source of bugs.

In kv:
MyClass@RelativeLayout

In Python
MyClass(Widget):
    pass

Instances of MyClass will behave as a widget, not a relative layout.  Is there any way to detect and prevent this?



Discussion:
Anytime a widget class is created, it is registered into the Factory. We could put a warning if we detected duplication / override.

Anytime a widget class is created, it is registered into the Factory. We could put a warning if we detected duplication / override.


Resolving patch:
diff --git a/kivy/factory.py b/kivy/factory.py
--- a/kivy/factory.py
+++ b/kivy/factory.py
@@ -65,9 +65,13 @@ def is_template(self, classname):
             return False
 
     def register(self, classname, cls=None, module=None, is_template=False,
-                 baseclasses=None, filename=None):
+                 baseclasses=None, filename=None, warn=False):
         '''Register a new classname referring to a real class or
-        class definition in a module.
+        class definition in a module. Warn, if True will emit a warning message
+        when a class is re-declared.
+
+        .. versionchanged:: 1.8.1
+            `warn` was added.
 
         .. versionchanged:: 1.7.0
             :attr:`baseclasses` and :attr:`filename` added
@@ -79,6 +83,14 @@ class definition in a module.
             raise ValueError(
                 'You must specify either cls= or module= or baseclasses =')
         if classname in self.classes:
+            if warn:
+                info = self.classes[classname]
+                Logger.warning('Factory: Ignored class "{}" re-declaration. '
+                'Current -  module: {}, cls: {}, baseclass: {}, filename: {}. '
+                'Ignored -  module: {}, cls: {}, baseclass: {}, filename: {}.'.
+                format(classname, info['module'], info['cls'],
+                       info['baseclasses'], info['filename'], module, cls,
+                       baseclasses, filename))
             return
         self.classes[classname] = {
             'module': module,
diff --git a/kivy/lang.py b/kivy/lang.py
--- a/kivy/lang.py
+++ b/kivy/lang.py
@@ -1601,11 +1601,12 @@ def load_string(self, string, **kwargs):
                 self.templates[name] = (cls, template, fn)
                 Factory.register(name,
                                  cls=partial(self.template, name),
-                                 is_template=True)
+                                 is_template=True, warn=True)
 
             # register all the dynamic classes
             for name, baseclasses in iteritems(parser.dynamic_classes):
-                Factory.register(name, baseclasses=baseclasses, filename=fn)
+                Factory.register(name, baseclasses=baseclasses, filename=fn,
+                                 warn=True)
 
             # create root object is exist
             if kwargs['rulesonly'] and parser.root:


Test patch: