Repository: kivy/kivy

Issue:
Test failures and hangs under Python 3.8
### Versions

* Python: 3.8.0
* OS: Arch Linux
* Kivy: 1.11.1
* Kivy installation method: setup.py

### Description

The test suite fails and hangs in test_clock with the following errors:

```
kivy/tests/test_clock.py FFF..FF.FF^C

=================================== FAILURES ===================================
_______________________ ClockTestCase.test_schedule_once _______________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_schedule_once>

    def test_schedule_once(self):
        from kivy.clock import Clock
        Clock.schedule_once(callback)
>       Clock.tick()

kivy/tests/test_clock.py:27:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
kivy/clock.py:591: in tick
    self._process_events()
kivy/_clock.pyx:384: in kivy._clock.CyClockBase._process_events
    cpdef _process_events(self):
kivy/_clock.pyx:414: in kivy._clock.CyClockBase._process_events
    raise
kivy/_clock.pyx:412: in kivy._clock.CyClockBase._process_events
    event.tick(self._last_tick)
kivy/_clock.pyx:154: in kivy._clock.ClockEvent.tick
    callback = self.get_callback()
kivy/_clock.pyx:86: in kivy._clock.ClockEvent.get_callback
    if callback.is_dead():
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[ReferenceError("weakly-referenced object no longer exists") raised in repr()] WeakMethod object at 0x7f04aa3a70a0>

    def is_dead(self):
        '''Returns True if the referenced callable was a bound method and
        the instance no longer exists. Otherwise, return False.
        '''
>       return self.proxy is not None and not bool(dir(self.proxy))
E       ReferenceError: weakly-referenced object no longer exists

kivy/weakmethod.py:56: ReferenceError
_________________ ClockTestCase.test_schedule_once_draw_after __________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_schedule_once_draw_after>

    def test_schedule_once_draw_after(self):
        from kivy.clock import Clock
        Clock.schedule_once(callback, 0)
        Clock.tick_draw()
        self.assertEqual(counter, 0)
>       Clock.tick()

kivy/tests/test_clock.py:42:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
kivy/clock.py:591: in tick
    self._process_events()
kivy/_clock.pyx:384: in kivy._clock.CyClockBase._process_events
    cpdef _process_events(self):
kivy/_clock.pyx:414: in kivy._clock.CyClockBase._process_events
    raise
kivy/_clock.pyx:412: in kivy._clock.CyClockBase._process_events
    event.tick(self._last_tick)
kivy/_clock.pyx:154: in kivy._clock.ClockEvent.tick
    callback = self.get_callback()
kivy/_clock.pyx:86: in kivy._clock.ClockEvent.get_callback
    if callback.is_dead():
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[ReferenceError("weakly-referenced object no longer exists") raised in repr()] WeakMethod object at 0x7f04aa390eb0>

    def is_dead(self):
        '''Returns True if the referenced callable was a bound method and
        the instance no longer exists. Otherwise, return False.
        '''
>       return self.proxy is not None and not bool(dir(self.proxy))
E       ReferenceError: weakly-referenced object no longer exists

kivy/weakmethod.py:56: ReferenceError
_________________ ClockTestCase.test_schedule_once_draw_before _________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_schedule_once_draw_before>

    def test_schedule_once_draw_before(self):
        from kivy.clock import Clock
        Clock.schedule_once(callback, -1)
        Clock.tick_draw()
        self.assertEqual(counter, 1)
        Clock.tick()
>       self.assertEqual(counter, 1)
E       AssertionError: 3 != 1

kivy/tests/test_clock.py:51: AssertionError
______________________ ClockTestCase.test_trigger_create _______________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_trigger_create>

    def test_trigger_create(self):
        from kivy.clock import Clock
        trigger = Clock.create_trigger(callback, 0)
        trigger()
        self.assertEqual(counter, 0)
>       Clock.tick()

kivy/tests/test_clock.py:89:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
kivy/clock.py:591: in tick
    self._process_events()
kivy/_clock.pyx:384: in kivy._clock.CyClockBase._process_events
    cpdef _process_events(self):
kivy/_clock.pyx:414: in kivy._clock.CyClockBase._process_events
    raise
kivy/_clock.pyx:412: in kivy._clock.CyClockBase._process_events
    event.tick(self._last_tick)
kivy/_clock.pyx:154: in kivy._clock.ClockEvent.tick
    callback = self.get_callback()
kivy/_clock.pyx:86: in kivy._clock.ClockEvent.get_callback
    if callback.is_dead():
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[ReferenceError("weakly-referenced object no longer exists") raised in repr()] WeakMethod object at 0x7f04aa3a70a0>

    def is_dead(self):
        '''Returns True if the referenced callable was a bound method and
        the instance no longer exists. Otherwise, return False.
        '''
>       return self.proxy is not None and not bool(dir(self.proxy))
E       ReferenceError: weakly-referenced object no longer exists

kivy/weakmethod.py:56: ReferenceError
_____________________ ClockTestCase.test_trigger_decorator _____________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_trigger_decorator>

    def test_trigger_decorator(self):
        from kivy.clock import Clock, triggered

        @triggered()
        def triggered_callback():
            callback(dt=0)

        triggered_callback()
        self.assertEqual(counter, 0)
        Clock.tick()
>       self.assertEqual(counter, 1)
E       AssertionError: 2 != 1

kivy/tests/test_clock.py:119: AssertionError
_____________________ ClockTestCase.test_trigger_interval ______________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_trigger_interval>

    def test_trigger_interval(self):
        from kivy.clock import Clock
        trigger = Clock.create_trigger(callback, 0, interval=True)
        trigger()
>       Clock.tick()

kivy/tests/test_clock.py:104:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
kivy/clock.py:591: in tick
    self._process_events()
kivy/_clock.pyx:384: in kivy._clock.CyClockBase._process_events
    cpdef _process_events(self):
kivy/_clock.pyx:414: in kivy._clock.CyClockBase._process_events
    raise
kivy/_clock.pyx:412: in kivy._clock.CyClockBase._process_events
    event.tick(self._last_tick)
kivy/_clock.pyx:154: in kivy._clock.ClockEvent.tick
    callback = self.get_callback()
kivy/_clock.pyx:86: in kivy._clock.ClockEvent.get_callback
    if callback.is_dead():
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[ReferenceError("weakly-referenced object no longer exists") raised in repr()] WeakMethod object at 0x7f04aa390eb0>

    def is_dead(self):
        '''Returns True if the referenced callable was a bound method and
        the instance no longer exists. Otherwise, return False.
        '''
>       return self.proxy is not None and not bool(dir(self.proxy))
E       ReferenceError: weakly-referenced object no longer exists

kivy/weakmethod.py:56: ReferenceError
________________________ ClockTestCase.test_unschedule _________________________

self = <kivy.tests.test_clock.ClockTestCase testMethod=test_unschedule>

    def test_unschedule(self):
        from kivy.clock import Clock
        Clock.schedule_once(callback)
>       Clock.unschedule(callback)

kivy/tests/test_clock.py:56:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
kivy/_clock.pyx:307: in kivy._clock.CyClockBase.unschedule
    cpdef unschedule(self, callback, all=True):
kivy/_clock.pyx:346: in kivy._clock.CyClockBase.unschedule
    if ev.get_callback() == callback:
kivy/_clock.pyx:86: in kivy._clock.ClockEvent.get_callback
    if callback.is_dead():
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[ReferenceError("weakly-referenced object no longer exists") raised in repr()] WeakMethod object at 0x7f04aa3a70a0>

    def is_dead(self):
        '''Returns True if the referenced callable was a bound method and
        the instance no longer exists. Otherwise, return False.
        '''
>       return self.proxy is not None and not bool(dir(self.proxy))
E       ReferenceError: weakly-referenced object no longer exists

kivy/weakmethod.py:56: ReferenceError
=============================== warnings summary ===============================
kivy/clock.py:1
  /build/python-kivy/src/Kivy-1.11.1/kivy/clock.py:1: DeprecationWarning: invalid escape sequence \*
    '''

kivy/lang/parser.py:44
  /build/python-kivy/src/Kivy-1.11.1/kivy/lang/parser.py:44: DeprecationWarning: invalid escape sequence \.
    lang_keyvalue = re.compile('([a-zA-Z_][a-zA-Z0-9_.]*\.[a-zA-Z0-9_.]+)')

kivy/lang/parser.py:45
  /build/python-kivy/src/Kivy-1.11.1/kivy/lang/parser.py:45: DeprecationWarning: invalid escape sequence \(
    lang_tr = re.compile('(_\()')

kivy/tests/test_imageloader.py:35
  /build/python-kivy/src/Kivy-1.11.1/kivy/tests/test_imageloader.py:35: DeprecationWarning: invalid escape sequence \d
    v0_FILE_RE = re.compile('^v0_(\d+)x(\d+)_' '([wxrgbycptA-F0-9]+)_'

kivy/tests/test_imageloader.py:36
  /build/python-kivy/src/Kivy-1.11.1/kivy/tests/test_imageloader.py:36: DeprecationWarning: invalid escape sequence \-
    '([0-9a-fA-F]{2})_' '([a-zA-Z0-9\-]+)_'

kivy/tests/test_imageloader.py:37
kivy/tests/test_imageloader.py:37
  /build/python-kivy/src/Kivy-1.11.1/kivy/tests/test_imageloader.py:37: DeprecationWarning: invalid escape sequence \-
    '([a-zA-Z0-9\-]+)_' '([a-zA-Z0-9\-]+)'

kivy/tests/test_imageloader.py:38
  /build/python-kivy/src/Kivy-1.11.1/kivy/tests/test_imageloader.py:38: DeprecationWarning: invalid escape sequence \.
    '\.([a-z]+)$')

kivy/core/text/markup.py:122
  /build/python-kivy/src/Kivy-1.11.1/kivy/core/text/markup.py:122: DeprecationWarning: invalid escape sequence \[
    s = re.split('(\[.*?\])', self.label)

kivy/parser.py:57
  /build/python-kivy/src/Kivy-1.11.1/kivy/parser.py:57: DeprecationWarning: invalid escape sequence \(
    res = re.match('rgba?\((.*)\)', text)

kivy/parser.py:62
  /build/python-kivy/src/Kivy-1.11.1/kivy/parser.py:62: DeprecationWarning: invalid escape sequence \
    for x in re.split(',\ ?', res.groups()[0])]

kivy/uix/behaviors/focus.py:463
  /build/python-kivy/src/Kivy-1.11.1/kivy/uix/behaviors/focus.py:463: SyntaxWarning: "is" with a literal. Did you mean "=="?
    walk_tree = 'walk' if focus_dir is 'focus_next' else 'walk_reverse'

kivy/uix/behaviors/focus.py:476
  /build/python-kivy/src/Kivy-1.11.1/kivy/uix/behaviors/focus.py:476: SyntaxWarning: "is" with a literal. Did you mean "=="?
    if focus_dir is 'focus_next':

kivy/uix/textinput.py:670
  /build/python-kivy/src/Kivy-1.11.1/kivy/uix/textinput.py:670: DeprecationWarning: invalid escape sequence \s
    re_indent = re.compile('^(\s*|)')

kivy/uix/textinput.py:3149
  /build/python-kivy/src/Kivy-1.11.1/kivy/uix/textinput.py:3149: DeprecationWarning: invalid escape sequence \
    '''Font size of the text in pixels.

kivy/uix/slider.py:73
  /build/python-kivy/src/Kivy-1.11.1/kivy/uix/slider.py:73: DeprecationWarning: invalid escape sequence \*
    '''Padding of the slider. The padding is used for graphical representation

/usr/lib/python3.8/site-packages/_pytest/mark/structures.py:321
  /usr/lib/python3.8/site-packages/_pytest/mark/structures.py:321: PytestUnknownMarkWarning: Unknown pytest.mark.incremental - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
    warnings.warn(

-- Docs: https://docs.pytest.org/en/latest/warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! KeyboardInterrupt !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
kivy/_clock.pyx:46: KeyboardInterrupt
(to show a full traceback on KeyboardInterrupt use --full-trace)
============ 7 failed, 34 passed, 17 warnings in 410.23s (0:06:50) =============
```



Discussion:

Resolving patch:
diff --git a/kivy/weakmethod.py b/kivy/weakmethod.py
--- a/kivy/weakmethod.py
+++ b/kivy/weakmethod.py
@@ -53,7 +53,10 @@ def is_dead(self):
             '''Returns True if the referenced callable was a bound method and
             the instance no longer exists. Otherwise, return False.
             '''
-            return self.proxy is not None and not bool(dir(self.proxy))
+            try:
+                return self.proxy is not None and not bool(dir(self.proxy))
+            except ReferenceError:
+                return True
 
         def __eq__(self, other):
             try:


Test patch: