head 1.1; access; symbols; locks; strict; comment @# @; 1.1 date 2026.10.28.05.09.43; author wiz; state Exp; branches; next ; commitid 83edb8H5rUQ7Bc1H; desc @@ 1.1 log @py-kivy: improve NetBSD support Based on https://codeberg.org/gnuhealth/mygnuhealth/wiki/MyGNUHealth-on-NetBSD-with-Kivy-Python While here, pull in upstream changes to fix compatibility with latest pytest-asyncio, and get the test suite to run. Bump PKGREVISION. @ text @$NetBSD$ https://github.com/kivy/kivy/commit/122596b656304aef81b53de2aebfc53bd24f0ecc --- kivy/tests/fixtures.py.orig 2026-10-28 04:29:19.784400323 +0000 +++ kivy/tests/fixtures.py @@@@ -1,4 +1,17 @@@@ import pytest 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 @@@@ apps = [] 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() @