Skip to content

feat: add sync subpackage for synchronous dependency resolution#3

Merged
ADR-007 merged 1 commit into
mainfrom
feat/sync-support
May 23, 2026
Merged

feat: add sync subpackage for synchronous dependency resolution#3
ADR-007 merged 1 commit into
mainfrom
feat/sync-support

Conversation

@ADR-007

@ADR-007 ADR-007 commented May 23, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a fastapi_depends_anywhere.sync subpackage so FastAPI dependencies can be resolved from a synchronous context (Celery tasks, Django views, CLI scripts) without managing an event loop manually.

New public API

Import Description
sync.with_fastapi_depends Resolve deps — returns plain sync callable
sync.iter_with_fastapi_depends Resolve deps for sync/async generators
sync.with_fastapi_lifecycle Run within FastAPI lifespan synchronously
sync.runnify_with_fastapi_depends Lifecycle + deps + sync execution in one decorator
sync.resolve_fastapi_depends Low-level sync context manager

How it works

A single daemon thread hosts a persistent asyncio event loop (lazily created via functools.cache). All sync-to-async bridging uses run_coroutine_threadsafe, so there is no per-call event loop creation overhead and no additional dependencies are required.

runnify_with_fastapi_depends runs lifecycle + dependency resolution in a single coroutine to avoid the deadlock that would result from nesting two separate run_coroutine_threadsafe calls.

Changes

  • fastapi_depends_anywhere/sync/ — new subpackage (5 files)
  • tests/test_sync/ — 41 new tests, 100% coverage of the sync subpackage
  • README.md — new Synchronous Execution section and updated API reference
  • fastapi_depends_anywhere/runners.py — removed now-redundant type: ignore (asyncer stubs available)

@github-actions

github-actions Bot commented May 23, 2026

Copy link
Copy Markdown

badge

Code Coverage Summary

Details
Filename                                      Stmts    Miss  Cover    Missing
------------------------------------------  -------  ------  -------  ---------
fastapi_depends_anywhere/__init__.py             11       0  100.00%
fastapi_depends_anywhere/_internal.py             7       0  100.00%
fastapi_depends_anywhere/config.py               20       0  100.00%
fastapi_depends_anywhere/core.py                 61       0  100.00%
fastapi_depends_anywhere/lifecycle.py            26       0  100.00%
fastapi_depends_anywhere/runners.py              18       0  100.00%
fastapi_depends_anywhere/sync/__init__.py         4       0  100.00%
fastapi_depends_anywhere/sync/_loop.py           18       0  100.00%
fastapi_depends_anywhere/sync/core.py            85       0  100.00%
fastapi_depends_anywhere/sync/lifecycle.py       36       0  100.00%
fastapi_depends_anywhere/sync/runners.py         37       0  100.00%
TOTAL                                           323       0  100.00%

Diff against main

Filename                                      Stmts    Miss  Cover
------------------------------------------  -------  ------  --------
fastapi_depends_anywhere/sync/__init__.py        +4       0  +100.00%
fastapi_depends_anywhere/sync/_loop.py          +18       0  +100.00%
fastapi_depends_anywhere/sync/core.py           +85       0  +100.00%
fastapi_depends_anywhere/sync/lifecycle.py      +36       0  +100.00%
fastapi_depends_anywhere/sync/runners.py        +37       0  +100.00%
TOTAL                                          +180       0  +100.00%

Results for commit: 2b7416a

Minimum allowed coverage is 90%

♻️ This comment has been updated with latest results

Introduces fastapi_depends_anywhere.sync with sync equivalents of all
core decorators, backed by a persistent background event loop thread.

New public API:
- sync.with_fastapi_depends     — resolve deps, returns plain sync callable
- sync.iter_with_fastapi_depends — resolve deps for sync/async generators
- sync.with_fastapi_lifecycle   — run within FastAPI lifespan synchronously
- sync.runnify_with_fastapi_depends — lifecycle + deps + sync execution
- sync.resolve_fastapi_depends  — low-level sync context manager

Implementation details:
- Single daemon thread hosts a persistent asyncio event loop (functools.cache)
- run_coroutine_threadsafe bridges sync callers to async dep resolution
- No per-call event loop overhead; no extra dependencies required
- Deadlock guard prevents calling _run_sync from within the loop thread
- runnify_with_fastapi_depends runs lifecycle+deps in one coroutine to avoid
  nested run_coroutine_threadsafe deadlock

Tests: 41 new tests with 100% coverage of the sync subpackage
Docs: README updated with Synchronous Execution section and API reference
@ADR-007 ADR-007 force-pushed the feat/sync-support branch from 8614bd1 to 2b7416a Compare May 23, 2026 08:56
@ADR-007 ADR-007 merged commit 0fceb47 into main May 23, 2026
6 checks passed
@ADR-007 ADR-007 deleted the feat/sync-support branch May 23, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant