summaryrefslogtreecommitdiff
path: root/dev-python/kivy/files/kivy-2.3.1-fix-pytest-9.x.x-issues.patch
blob: 845358ec06566b5bef04d28cc3fa0ff50de58db2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
From: Pavel Sobolev <contact@paveloom.dev>
Subject: [PATCH] Fix pytest>=9.x.x issues

Upstream-Commit: https://github.com/kivy/kivy/commit/122596b656304aef81b53de2aebfc53bd24f0ecc
Signed-off-by: Pavel Sobolev <contact@paveloom.dev>

--- a/kivy/tests/common.py
+++ b/kivy/tests/common.py
@@ -498,7 +498,10 @@ def async_run(func=None, app_cls_func=None):
         if kivy_eventloop == 'asyncio':
             try:
                 import pytest_asyncio
-                return pytest.mark.asyncio(pytest_asyncio.fixture(func))
+                # In pytest 9, marks on fixtures have no effect.
+                # We only need to mark the async test function,
+                # not turn it into a fixture.
+                return pytest.mark.asyncio(func)
             except ImportError:
                 return pytest.mark.skip(
                     reason='KIVY_EVENTLOOP == "asyncio" but '
--- a/kivy/tests/conftest.py
+++ b/kivy/tests/conftest.py
@@ -6,7 +6,7 @@ kivy_eventloop = os.environ.get('KIVY_EVENTLOOP', 'asyncio')
 try:
     from .fixtures import kivy_app, kivy_clock, kivy_metrics, \
         kivy_exception_manager
-except SyntaxError:
+except (SyntaxError, ImportError):
     # async app tests would be skipped due to async_run forcing it to skip so
     # it's ok to fail here as it won't be used anyway
     pass
--- a/kivy/tests/fixtures.py
+++ b/kivy/tests/fixtures.py
@@ -1,4 +1,17 @@
 import pytest
+import os
+import sys
+# Choose async fixture decorator based on KIVY_EVENTLOOP and available plugins
+_env_eventloop = os.environ.get('KIVY_EVENTLOOP', 'asyncio')
+if _env_eventloop == 'asyncio':
+    try:
+        import pytest_asyncio
+        _ASYNC_FIXTURE_DECORATOR = pytest_asyncio.fixture
+    except ImportError:  # fallback if pytest-asyncio is missing
+        _ASYNC_FIXTURE_DECORATOR = pytest.fixture
+else:
+    # For trio/other event loops, let the active plugin handle async functions
+    _ASYNC_FIXTURE_DECORATOR = pytest.fixture
 import gc
 import weakref
 import time
@@ -66,13 +79,28 @@ def kivy_exception_manager():
 apps = []
 
 
-@pytest.fixture()
+# Async fixture, decorator chosen based on available plugin
+@_ASYNC_FIXTURE_DECORATOR()
 async def kivy_app(request, nursery):
+    from kivy.base import stopTouchApp
+    from kivy.app import App
+
     gc.collect()
+    # Clean up any previous app that might still be hanging around
     if apps:
         last_app, last_request = apps.pop()
-        assert last_app() is None, \
-            'Memory leak: failed to release app for test ' + repr(last_request)
+        leaked = last_app()
+        if leaked is not None:
+            # Log warning but don't fail - pytest 9 async fixtures may not
+            # guarantee teardown completion before next test setup
+            print(
+                f"\nWarning: Previous app not released: {last_request}",
+                file=sys.stderr,
+            )
+            stopTouchApp()
+            App._running_app = None
+            gc.collect()
+            del leaked
 
     from os import environ
     environ['KIVY_USE_DEFAULTCONFIG'] = '1'
@@ -96,10 +124,6 @@ async def kivy_app(request, nursery):
 
     kivy_eventloop = environ.get('KIVY_EVENTLOOP', 'asyncio')
     if kivy_eventloop == 'asyncio':
-        pytest.importorskip(
-            'pytest_asyncio',
-            reason='KIVY_EVENTLOOP == "asyncio" but '
-                   '"pytest_asyncio" is not installed')
         async_lib = 'asyncio'
     elif kivy_eventloop == 'trio':
         pytest.importorskip(
@@ -149,7 +173,10 @@ async def kivy_app(request, nursery):
 
     yield app
 
+    # Comprehensive cleanup for pytest 9 async fixture compatibility
+    from kivy.app import App
     stopTouchApp()
+    App._running_app = None
 
     ts = time.perf_counter()
     while not app.app_has_stopped:
@@ -161,9 +188,14 @@ async def kivy_app(request, nursery):
         Window.remove_widget(child)
     context.pop()
 
-    # release all the resources
+    # Aggressively release all resources
     del context
     LoggerHistory.clear_history()
+
+    # Store weakref for next test to check cleanup
     apps.append((weakref.ref(app), request))
     del app
-    gc.collect()
+
+    # Force garbage collection multiple times to ensure cleanup
+    for _ in range(3):
+        gc.collect()
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -15,3 +15,4 @@ requires = [
 [tool.pytest.ini_options]
 addopts = "--benchmark-skip --benchmark-warmup=on --benchmark-warmup-iterations=5 --benchmark-disable-gc --benchmark-name=short --benchmark-sort=mean --benchmark-group-by=fullfunc --benchmark-storage=.benchmarks-kivy --benchmark-save=kivy"
 markers = "incremental: mark a test as incremental."
+asyncio_mode = "auto"