{"library":"synchronicity","title":"Synchronicity","description":"Synchronicity is a Python library designed to simplify the development of libraries that need to offer both synchronous (blocking) and asynchronous (non-blocking) APIs from a single async implementation. It achieves this by creating an event loop on a separate thread, wrapping functions, generators, and classes, allowing them to be called synchronously or asynchronously via a `.aio` attribute. The current version is 0.12.1, and it appears to be actively maintained with regular updates.","status":"active","version":"0.12.1","language":"en","source_language":"en","source_url":"https://github.com/modal-labs/synchronicity","tags":["async","sync","concurrency","utilities","library-development","event-loop","asyncio"],"install":[{"cmd":"pip install synchronicity","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"note":"The primary class for wrapping functions and classes is Synchronizer.","wrong":"import synchronicity","symbol":"Synchronizer","correct":"from synchronicity import Synchronizer"}],"quickstart":{"code":"import asyncio\nfrom synchronicity import Synchronizer\n\nsynchronizer = Synchronizer()\n\n@synchronizer.wrap\nasync def my_async_function(x):\n    await asyncio.sleep(0.1)\n    return x**2\n\n# --- Synchronous usage (blocking) ---\nprint(f\"Synchronous call: my_async_function(5) = {my_async_function(5)}\") # Blocks until result is ready\n\n# --- Asynchronous usage (non-blocking) ---\nasync def main_async():\n    print(f\"Asynchronous call (awaiting): await my_async_function.aio(10) = {await my_async_function.aio(10)}\")\n\n    # Example with multiple concurrent calls from an async context\n    results = await asyncio.gather(\n        my_async_function.aio(2),\n        my_async_function.aio(3),\n        my_async_function.aio(4)\n    )\n    print(f\"Concurrent async calls: {results}\")\n\nasyncio.run(main_async())","lang":"python","description":"This quickstart demonstrates how to define an asynchronous function and then use `Synchronizer` to expose both a synchronous (blocking) interface and an asynchronous (`.aio`) interface for it. The synchronous call blocks until completion, while the asynchronous call can be awaited within an async context."},"warnings":[{"fix":"Access class attributes via getter methods or `@property` decorators on the original (unwrapped) class definition.","message":"When a class is wrapped by `synchronizer.wrap`, instances become proxies. Direct access to attributes of the original class might not work; you may need to define getter methods or `@properties` on the original class for them to be reachable on the wrapper.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure all I/O-bound or long-running operations within your original async functions are truly asynchronous (e.g., use `await` with async-compatible libraries like `aiohttp` instead of `requests`).","message":"Synchronicity isolates library execution to its own event loop and thread, which helps prevent accidental blocking of a user's main event loop. However, any long-running, non-async calls within *your* original async functions (or outside Synchronicity's wrapper) can still block the event loop where they execute, negating async benefits.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Be mindful of this overhead for extremely performance-sensitive, CPU-bound tasks where thread isolation isn't strictly necessary. Profile your application to understand performance implications.","message":"All synchronized code runs on a different thread and event loop. While this provides isolation benefits, it introduces minor overhead due to thread switching and inter-thread communication.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Separate your wrapper and implementation code into different modules. Use the `python -m synchronicity.type_stubs` tool to generate `.pyi` files for your 'wrapper modules' to provide static type support.","message":"Wrapping classes with `synchronizer.wrap` creates a new class (with the same name). This can lead to unexpected type-checking issues or runtime errors if you mix usage of the original class and the wrapped class without careful consideration of the type transformation.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If you need to synchronize a context manager, define it as a class and apply `@synchronizer.wrap` to the class, or use `@synchronizer.asynccontextmanager` for async context manager functions.","message":"Synchronicity's `wrap` decorator works for classes that are context managers, but it does not support wrapping functions that return context managers.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-05T00:00:00.000Z","next_check":"2026-07-04T00:00:00.000Z"}