Repository: kivy/kivy

Issue:
Can't use unit values as strings in recyclegridlayout's `default_size`
**Software Versions**
* Python: 3.9
* OS: Linux
* Kivy: 554e98fb7 (master)
* Kivy installation method:

**Describe the bug**

When trying to set `default_size` as a value such as `0, '50dp'` the layout crashes when trying to compare the string value with an integer, instead of converting them first to a number using the metrics.

**Expected behavior**
The value should be converted to a integer, using the metrics system, before trying to use it for computing layout constraints.

**To Reproduce**
run the following code.
```python
from kivy.app import App
from kivy.lang import Builder

KV = '''
BoxLayout:
    orientation: 'vertical'

    Spinner:
        id: orientation
        text: 'lr-tb'
        values: ['lr-tb', 'tb-lr', 'rl-tb', 'tb-rl', 'lr-bt', 'bt-lr', 'rl-bt', 'bt-rl']
        size_hint_y: None
        height: dp(40)
    RecycleView:
        data: [{'text': f"cell {n}"} for n in range(100)]
        viewclass: 'Label'

        RecycleGridLayout:
            cols: 3
            orientation: orientation.text or 'lr-tb'
            default_size: 0, '56dp'    # FIXME this should work, but for now we need to do dp(56) instead
            default_size_hint: 1, None
            size_hint_y: None
            height: self.minimum_height
'''  # noqa


class Application(App):
    def build(self):
        return Builder.load_string(KV)


if __name__ == "__main__":
    Application().run()
```

Willo produce an output similar to:
```
gabriel@gryphon:/tmp> p test_rv.py
[WARNING] [Config      ] Older configuration version detected (21 instead of 22)
[WARNING] [Config      ] Upgrading configuration in progress.
[DEBUG  ] [Config      ] Upgrading from 21 to 22
[INFO   ] [Logger      ] Record log in /home/gabriel/.kivy/logs/kivy_21-01-17_2.txt
[INFO   ] [Kivy        ] v2.1.0.dev0
[INFO   ] [Kivy        ] Installed at "/home/gabriel/dev/kivy/kivy/__init__.py"
[INFO   ] [Python      ] v3.9.1 (default, Dec  8 2020, 03:24:52) 
[GCC 7.5.0]
[INFO   ] [Python      ] Interpreter at "/home/gabriel/.virtualenvs/kivy3.9-dev/bin/python"
[INFO   ] [Factory     ] 187 symbols loaded
[DEBUG  ] [Cache       ] register <kv.resourcefind> with limit=None, timeout=60
[DEBUG  ] [Cache       ] register <kv.lang> with limit=None, timeout=None
[DEBUG  ] [Cache       ] register <kv.image> with limit=None, timeout=60
[DEBUG  ] [Cache       ] register <kv.atlas> with limit=None, timeout=None
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_pil (img_ffpyplayer ignored)
[DEBUG  ] [Cache       ] register <kv.texture> with limit=1000, timeout=60
[DEBUG  ] [Cache       ] register <kv.shader> with limit=1000, timeout=3600
[DEBUG  ] [App         ] Loading kv </tmp/application.kv>
[DEBUG  ] [App         ] kv </tmp/application.kv> not found
[INFO   ] [Window      ] Provider: sdl2
[INFO   ] [GL          ] Using the "OpenGL" graphics system
[INFO   ] [GL          ] Backend used <sdl2>
[INFO   ] [GL          ] OpenGL version <b'3.0 Mesa 20.0.8'>
[INFO   ] [GL          ] OpenGL vendor <b'Intel Open Source Technology Center'>
[INFO   ] [GL          ] OpenGL renderer <b'Mesa DRI Intel(R) HD Graphics 620 (KBL GT2)'>
[INFO   ] [GL          ] OpenGL parsed version: 3, 0
[INFO   ] [GL          ] Shading version <b'1.30'>
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <32>
[DEBUG  ] [Shader      ] Fragment compiled successfully
[DEBUG  ] [Shader      ] Vertex compiled successfully
[DEBUG  ] [ImageSDL2   ] Load </home/gabriel/dev/kivy/kivy/data/glsl/default.png>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[DEBUG  ] [Text        ] Provider <pango> ignored by config
[INFO   ] [Text        ] Provider: sdl2(['text_pango'] ignored)
[DEBUG  ] [Resource    ] add </usr/share/fonts> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/woff> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/woff/font-awesome> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/woff/elusive-icons> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/type1> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/type1/gsfonts> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/type1/texlive-fonts-recommended> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/svg> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/svg/font-awesome> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/svg/elusive-icons> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/cmap> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/cMap> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/opentype> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/opentype/linux-libertine> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/opentype/noto> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/opentype/font-awesome> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/ttf-khmeros-core> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/droid> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/ttf-bitstream-vera> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/lyx> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/gentium> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/dejavu> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/lao> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/liberation> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/ubuntu> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/abyssinica> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/emojione> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/tlwg> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/fonts-guru-extra> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/freefont> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/lato> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/kacst-one> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/tibetan-machine> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/nanum> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/openoffice> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/padauk> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/noto> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/liberation2> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/lohit-punjabi> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/crosextra> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/sinhala> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/gentium-basic> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/ancient-scripts> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/font-awesome> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/unifont> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/elusive-icons> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/kacst> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/truetype/takao-gothic> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/eot> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/eot/font-awesome> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/eot/elusive-icons> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11/Type1> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11/misc> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11/encodings> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11/encodings/large> in path list
[DEBUG  ] [Resource    ] add </usr/share/fonts/X11/util> in path list
[DEBUG  ] [Resource    ] add </usr/local/share/fonts> in path list
[DEBUG  ] [Resource    ] add </home/gabriel/.local/share/fonts> in path list
[DEBUG  ] [Resource    ] add </home/gabriel/dev/kivy/kivy/data/fonts> in path list
[DEBUG  ] [Base        ] Create provider from mouse
[DEBUG  ] [Base        ] Create provider from probesysfs,match=LGDisplay
[DEBUG  ] [ProbeSysfs  ] using probesysfs!
[DEBUG  ] [ProbeSysfs  ] found device: Melfas LGDisplay Incell Touch at /dev/input/event17
[INFO   ] [ProbeSysfs  ] device match: /dev/input/event17
[INFO   ] [MTD         ] Read event from </dev/input/event17>
[DEBUG  ] [ProbeSysfs  ] found device: SynPS/2 Synaptics TouchPad at /dev/input/event5
[INFO   ] [Base        ] Start application main loop
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
[INFO   ] [MTD         ] </dev/input/event17> range position X is 0 - 4095
   File "/tmp/test_rv.py", line 34, in <module>
[INFO   ] [MTD         ] </dev/input/event17> range position Y is 0 - 4095
[INFO   ] [MTD         ] </dev/input/event17> range touch major is 0 - 0
[INFO   ] [MTD         ] </dev/input/event17> range touch minor is 0 - 0
     Application().run()
[INFO   ] [MTD         ] </dev/input/event17> range pressure is 0 - 255
   File "/home/gabriel/dev/kivy/kivy/app.py", line 950, in run
[INFO   ] [MTD         ] </dev/input/event17> axes invertion: X is 0, Y is 0
[INFO   ] [MTD         ] </dev/input/event17> rotation set to 0
     runTouchApp()
   File "/home/gabriel/dev/kivy/kivy/base.py", line 582, in runTouchApp
     EventLoop.mainloop()
   File "/home/gabriel/dev/kivy/kivy/base.py", line 347, in mainloop
     self.idle()
   File "/home/gabriel/dev/kivy/kivy/base.py", line 387, in idle
     Clock.tick()
   File "/home/gabriel/dev/kivy/kivy/clock.py", line 733, in tick
     self.post_idle(ts, self.idle())
   File "/home/gabriel/dev/kivy/kivy/clock.py", line 776, in post_idle
     self._process_events()
   File "kivy/_clock.pyx", line 616, in kivy._clock.CyClockBase._process_events
   File "kivy/_clock.pyx", line 649, in kivy._clock.CyClockBase._process_events
   File "kivy/_clock.pyx", line 645, in kivy._clock.CyClockBase._process_events
   File "kivy/_clock.pyx", line 218, in kivy._clock.ClockEvent.tick
   File "/home/gabriel/dev/kivy/kivy/uix/recycleview/__init__.py", line 238, in refresh_views
     lm.compute_layout(data, f)
   File "/home/gabriel/dev/kivy/kivy/uix/recyclegridlayout.py", line 148, in compute_layout
     self._fill_rows_cols_sizes()
   File "/home/gabriel/dev/kivy/kivy/uix/recyclegridlayout.py", line 72, in _fill_rows_cols_sizes
     rows[row] = nmax(rows[row], h)
   File "/home/gabriel/dev/kivy/kivy/uix/gridlayout.py", line 105, in nmax
     return max(args)
 TypeError: '>' not supported between instances of 'str' and 'int'
```


Discussion:
Hmm, yeah, [these](https://github.com/kivy/kivy/blob/master/kivy/uix/recyclelayout.py#L25) and a few others needs to be converted from `ObjectProperty` to `ReferenceListProperty` or `VariableListProperty` (although the latter doesn't support `None` for the list values 🤷 ).

Resolving patch:
diff --git a/kivy/uix/recyclelayout.py b/kivy/uix/recyclelayout.py
--- a/kivy/uix/recyclelayout.py
+++ b/kivy/uix/recyclelayout.py
@@ -11,7 +11,9 @@
 
 from kivy.uix.recycleview.layout import RecycleLayoutManagerBehavior
 from kivy.uix.layout import Layout
-from kivy.properties import ObjectProperty, StringProperty
+from kivy.properties import (
+    ObjectProperty, StringProperty, ReferenceListProperty, NumericProperty
+)
 from kivy.factory import Factory
 
 __all__ = ('RecycleLayout', )
@@ -22,19 +24,152 @@ class RecycleLayout(RecycleLayoutManagerBehavior, Layout):
     RecycleLayout provides the default layout for RecycleViews.
     """
 
-    default_size = ObjectProperty((100, 100))
-    '''size as in w, h. They each can be None.
+    default_width = NumericProperty(100, allownone=True)
+    '''Default width for items
+
+    :attr:`default_width` is a NumericProperty and default to 100
+    '''
+    default_height = NumericProperty(100, allownone=True)
+    '''Default height for items
+
+    :attr:`default_height` is a :class:`~kivy.properties.NumericProperty` and
+    default to 100.
     '''
+    default_size = ReferenceListProperty(default_width, default_height)
+    '''size (width, height). Each value can be None.
+
+    :attr:`default_size` is an :class:`~kivy.properties.ReferenceListProperty`
+    to [:attr:`default_width`, :attr:`default_height`].
+    '''
+    default_size_hint_x = NumericProperty(None, allownone=True)
+    '''Default size_hint_x for items
+
+    :attr:`default_size_hint_x` is a :class:`~kivy.properties.NumericProperty`
+    and default to None.
+    '''
+    default_size_hint_y = NumericProperty(None, allownone=True)
+    '''Default size_hint_y for items
+
+    :attr:`default_size_hint_y` is a :class:`~kivy.properties.NumericProperty`
+    and default to None.
+    '''
+    default_size_hint = ReferenceListProperty(
+        default_size_hint_x, default_size_hint_y
+    )
+    '''size (width, height). Each value can be None.
+
+    :attr:`default_size_hint` is an
+    :class:`~kivy.properties.ReferenceListProperty` to
+    [:attr:`default_size_hint_x`, :attr:`default_size_hint_y`].
+    '''
+
     key_size = StringProperty(None, allownone=True)
-    default_size_hint = ObjectProperty((None, None))
+    '''If set, which key in the dict should be used to set the size property of
+    the item.
+
+    :attr:`key_size` is a :class:`~kivy.properties.StringProperty` and defaults
+    to None.
+    '''
     key_size_hint = StringProperty(None, allownone=True)
-    default_size_hint_min = ObjectProperty((None, None))
+    '''If set, which key in the dict should be used to set the size_hint
+    property of the item.
+
+    :attr:`key_size_hint` is a :class:`~kivy.properties.StringProperty` and
+    defaults to None.
+    '''
+
     key_size_hint_min = StringProperty(None, allownone=True)
-    default_size_hint_max = ObjectProperty((None, None))
+    '''If set, which key in the dict should be used to set the size_hint_min
+    property of the item.
+
+    :attr:`key_size_hint_min` is a :class:`~kivy.properties.StringProperty` and
+    defaults to None.
+    '''
+    default_size_hint_x_min = NumericProperty(None, allownone=True)
+    '''Default value for size_hint_x_min of items
+
+    :attr:`default_pos_hint_x_min` is a
+    :class:`~kivy.properties.NumericProperty` and defaults to None.
+    '''
+    default_size_hint_y_min = NumericProperty(None, allownone=True)
+    '''Default value for size_hint_y_min of items
+
+    :attr:`default_pos_hint_y_min` is a
+    :class:`~kivy.properties.NumericProperty` and defaults to None.
+    '''
+    default_size_hint_min = ReferenceListProperty(
+        default_size_hint_x_min,
+        default_size_hint_y_min
+    )
+    '''Default value for size_hint_min of items
+
+    :attr:`default_size_min` is a
+    :class:`~kivy.properties.ReferenceListProperty` to
+    [:attr:`default_size_hint_x_min`, :attr:`default_size_hint_y_min`].
+    '''
+
     key_size_hint_max = StringProperty(None, allownone=True)
+    '''If set, which key in the dict should be used to set the size_hint_max
+    property of the item.
+
+    :attr:`key_size_hint_max` is a :class:`~kivy.properties.StringProperty` and
+    defaults to None.
+    '''
+    default_size_hint_x_max = NumericProperty(None, allownone=True)
+    '''Default value for size_hint_x_max of items
+
+    :attr:`default_pos_hint_x_max` is a
+    :class:`~kivy.properties.NumericProperty` and defaults to None.
+    '''
+    default_size_hint_y_max = NumericProperty(None, allownone=True)
+    '''Default value for size_hint_y_max of items
+
+    :attr:`default_pos_hint_y_max` is a
+    :class:`~kivy.properties.NumericProperty` and defaults to None.
+    '''
+    default_size_hint_max = ReferenceListProperty(
+        default_size_hint_x_max,
+        default_size_hint_y_max
+    )
+    '''Default value for size_hint_max of items
+
+    :attr:`default_size_max` is a
+    :class:`~kivy.properties.ReferenceListProperty` to
+    [:attr:`default_size_hint_x_max`, :attr:`default_size_hint_y_max`].
+    '''
+
     default_pos_hint = ObjectProperty({})
+    '''Default pos_hint value for items
+
+    :attr:`default_pos_hint` is a :class:`~kivy.properties.DictProperty` and
+    defaults to {}.
+    '''
     key_pos_hint = StringProperty(None, allownone=True)
-    initial_size = ObjectProperty((100, 100))
+    '''If set, which key in the dict should be used to set the pos_hint of
+    items.
+
+    :attr:`key_pos_hint` is a :class:`~kivy.properties.StringProperty` and
+    defaults to None.
+    '''
+
+    initial_width = NumericProperty(100)
+    '''Initial width for the items.
+
+    :attr:`initial_width` is a :class:`~kivy.properties.NumericProperty` and
+    defaults to 100.
+    '''
+    initial_height = NumericProperty(100)
+    '''Initial height for the items.
+
+    :attr:`initial_height` is a :class:`~kivy.properties.NumericProperty` and
+    defaults to 100.
+    '''
+    initial_size = ReferenceListProperty(initial_width, initial_height)
+    '''Initial size of items
+
+    :attr:`initial_size` is a :class:`~kivy.properties.ReferenceListProperty`
+    to [:attr:`initial_width`, :attr:`initial_height`].
+    '''
 
     view_opts = []
 


Test patch: