Uh oh!
There was an error while loading. Please reload this page.
feat!: remove all sync APIs (Phase 2) - #423
Conversation
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
BREAKING CHANGE: All synchronous API classes have been removed. The async classes are now the only implementation and have been renamed to drop the Async prefix: - AsyncNextcloud -> Nextcloud - AsyncNextcloudApp -> NextcloudApp - AsyncFilesAPI -> FilesAPI - AsyncTalkBot -> TalkBot - anc_app -> nc_app - atalk_bot_msg -> talk_bot_msg Backward compatibility aliases are provided and will be removed in v1.0.0. Removed ~2,950 lines of duplicated sync code.
- Convert test conftest to async (fixtures, setup/teardown) - Remove 174 sync test functions (duplicates of async tests) - Convert unique sync-only tests to async - Skip CalDAV tests (not available in async-only mode) - Convert test entry scripts (_install.py, _talk_bot.py, etc.) - Update README to reflect async-only API
The test conftest opens HTTP connections at import time via ``run_until_complete`` to check ``app_api`` capability and (later) NC version. After the sync removal in v0.31.0 those connections live in niquests AsyncSession pools bound to whatever event loop ran the ``run_until_complete`` call. pytest-asyncio runs the actual tests on a different loop, so the first request hits a stale pooled connection and fails with:: RuntimeError: got Future <Future pending> attached to a different loop Recreate the session adapters (``init_adapter(restart=True)`` and ``init_adapter_dav(restart=True)``) right after the import-time check so pytest's loop populates fresh empty pools lazily on first use. Apply the same reset after the version lookup in ``pytest_collection_modifyitems``, and short-circuit that hook entirely when no test carries the ``require_nc`` marker.
9a22a76 to
65cbca3Compare…udApp After Phase 2 made ``NextcloudApp.log`` and ``NextcloudApp.set_init_status`` async, two ExApp helpers kept calling them as if they were sync — silently producing ``RuntimeWarning: coroutine '...' was never awaited`` and dropping the operation entirely. ``setup_nextcloud_logging`` is supposed to stream every Python log record into ``nextcloud.log``, and ``fetch_models_task`` is the default ``/init`` handler that reports model-download progress back to AppAPI; both were no-ops. logger.py * Capture the running event loop at ``setup_nextcloud_logging`` time and stash it on the handler. ``logging.Handler.emit`` is sync and can be called from any thread, so each record is dispatched to the captured loop with ``asyncio.run_coroutine_threadsafe``. Fire-and-forget, since logging must not block the caller. If the function is invoked outside a running loop (no-async setup), an inert handler is installed. integration_fastapi.py * ``fetch_models_task`` gains an optional ``loop`` parameter and is wrapped by a small ``_ProgressReporter`` that schedules ``set_init_status`` on the captured loop and waits with a 30s timeout for ordered progress updates. * The ``/init`` handler now captures ``asyncio.get_running_loop()`` and forwards it to the BackgroundTasks worker thread, where there is no loop of its own. * Inner ``__fetch_model_as_file`` / ``__fetch_model_as_snapshot`` accept the reporter callable in place of the bare ``NextcloudApp`` and use it for every progress tick. Bumps the version to 0.31.0.dev0 and adds a CHANGELOG entry covering the breaking sync removal, these regressions, and the conftest event-loop reset.
Phase 2 left a number of tests and examples calling the deprecated
``AsyncNextcloud`` / ``AsyncNextcloudApp`` / ``AsyncTalkBot`` / ``atalk_bot_msg``
aliases that now just point at the canonical async classes. The aliases stay
in place for users (and remain covered by ``tests/_install_async.py`` and
``tests/_install_only_enabled_handler_async.py`` which are the contract
tests for the backward-compat surface), but internal call sites should not
look like they need them.
* tests/actual_tests/misc_test.py — Async{Nextcloud,NextcloudApp} -> {Nextcloud,NextcloudApp}
* tests/actual_tests/talk_bot_test.py — AsyncTalkBot -> TalkBot
* examples/as_client/files/{listing,upload,find,download}.py — AsyncNextcloud -> Nextcloud
* examples/as_app/talk_bot{,_ai}/lib/main.py — atalk_bot_msg -> talk_bot_msg
Fix the ``examples/as_app/to_gif`` ExApp, which was a real regression: every
``nc.log`` / ``nc.files.*`` / ``nc.notifications.*`` / ``nc.ui.*`` call became
an unawaited coroutine after Phase 2. Convert ``convert_video_to_gif`` and
``enabled_handler`` to coroutines, await every call site, and offload the
CPU/IO-heavy ``cv2`` + ``imageio`` + ``pygifsicle`` pipeline to a sync helper
that runs via ``asyncio.to_thread`` so the event loop stays responsive.The previous fix piped every progress update through ``asyncio.run_coroutine_threadsafe(nc.set_init_status(...), loop)``. That works at runtime but breaks ``tests_unit/test_fetch_model_file.py``, which drives ``fetch_models_task`` from a regular sync test with a ``MagicMock`` ``nc``: there is no event loop and no real coroutine, so the loop-scheduling branch short-circuits and ``nc.set_init_status`` was never called at all. Make ``_ProgressReporter.__call__`` invoke ``nc.set_init_status`` unconditionally so mocks (and any future sync ``NextcloudApp`` shim) observe the call. Inspect the return value: if it is a coroutine, schedule it on the captured loop and wait for the result; if there is no loop available, ``coro.close()`` to avoid the "coroutine never awaited" warning; if it is not a coroutine at all (the mock case) just drop it. Also drop the empty ``error`` argument when calling ``set_init_status`` to keep the call signature identical to the pre-Phase-2 behaviour, so existing tests that assert ``call(100)`` rather than ``call(100, "")`` keep matching.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@## main #423 +/- ##
==========================================
- Coverage 94.67% 94.06% -0.61%
==========================================
Files 48 48 Lines 5636 4585 -1051 ==========================================
- Hits 5336 4313 -1023 + Misses 300 272 -28
🚀 New features to boost your workflow:
|
Add unit tests for the new code paths introduced when the logger handler and ``fetch_models_task`` were taught to schedule async :py:meth:`NextcloudApp.log` and :py:meth:`NextcloudApp.set_init_status` calls onto a captured event loop. These exercise the branches that the integration suite cannot reach: * ``_ProgressReporter`` with no captured loop (mock and real coroutine paths), with a closed loop, with errors raised by the scheduled coroutine, and the happy-path scheduling against a running loop via ``asyncio.to_thread``. * ``_NextcloudLogsHandler.emit`` short-circuiting when no loop / closed loop is attached, plus the dispatch path that observes the formatted record landing on the captured loop. * ``setup_nextcloud_logging`` capturing the running loop when called from async context and returning an inert handler when called outside one. * ``fetch_models_task`` accepting an explicit ``loop`` argument and falling back to ``asyncio.get_running_loop`` when invoked via ``to_thread``. Restores codecov coverage above the 1% drop threshold.
The sync removal is the project's first stable major release rather than a 0.31.0 minor — there is no remaining sync surface to refine, and the async-only API is the long-term contract. Move the version and changelog header to ``1.0.0`` accordingly, and drop the now self-referential "will be removed in v1.0.0" line about backward-compat aliases (they remain in this release for migration convenience and will be dropped in the next major).
Summary
Asyncprefix (AsyncNextcloud->Nextcloud, etc.)AsyncNextcloud = Nextcloud,AsyncNextcloudApp = NextcloudApp,AsyncTalkBot = TalkBot,anc_app = nc_app,atalk_bot_msg = talk_bot_msgBREAKING CHANGE: All sync APIs removed. Use async equivalents.
Test plan