{"id":2973,"library":"janus","title":"janus","description":"janus is a Python library providing mixed synchronous and asynchronous queues to facilitate communication between classic threaded code and asyncio tasks. It offers `Queue`, `LifoQueue`, and `PriorityQueue` implementations, each with distinct synchronous (`.sync_q`) and asynchronous (`.async_q`) interfaces. The current version is 2.0.0, and the library is actively maintained to support the latest Python versions.","status":"active","version":"2.0.0","language":"en","source_language":"en","source_url":"https://github.com/aio-libs/janus","tags":["asyncio","threading","queue","concurrency","sync-async"],"install":[{"cmd":"pip install janus","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"Queue","correct":"from janus import Queue"},{"symbol":"LifoQueue","correct":"from janus import LifoQueue"},{"symbol":"PriorityQueue","correct":"from janus import PriorityQueue"},{"symbol":"SyncQueue","correct":"from janus import SyncQueue"},{"symbol":"AsyncQueue","correct":"from janus import AsyncQueue"},{"note":"As of v1.1.0, janus re-exports its own specific QueueEmpty/Full exceptions, which should be used for janus queues.","wrong":"from queue import Empty; # For janus.SyncQueue","symbol":"SyncQueueEmpty","correct":"from janus import SyncQueueEmpty"},{"note":"As of v1.1.0, janus re-exports its own specific QueueEmpty/Full exceptions, which should be used for janus queues.","wrong":"from asyncio.queues import QueueEmpty; # For janus.AsyncQueue","symbol":"AsyncQueueEmpty","correct":"from janus import AsyncQueueEmpty"}],"quickstart":{"code":"import asyncio\nimport threading\nimport janus\n\ndef threaded_producer(sync_q: janus.SyncQueue[int]) -> None:\n    print(\"Thread: Starting producer\")\n    for i in range(5):\n        print(f\"Thread: Putting {i}\")\n        sync_q.put(i)\n    sync_q.join() # Wait for all items to be processed by async_coro\n    print(\"Thread: Producer finished and joined\")\n\nasync def async_consumer(async_q: janus.AsyncQueue[int]) -> None:\n    print(\"Async: Starting consumer\")\n    for _ in range(5):\n        val = await async_q.get()\n        print(f\"Async: Got {val}\")\n        async_q.task_done()\n    print(\"Async: Consumer finished\")\n\nasync def main() -> None:\n    queue: janus.Queue[int] = janus.Queue()\n    loop = asyncio.get_running_loop()\n\n    # Run the synchronous producer in a separate thread\n    producer_thread = threading.Thread(target=threaded_producer, args=(queue.sync_q,))\n    producer_thread.start()\n\n    # Run the asynchronous consumer\n    await async_consumer(queue.async_q)\n\n    producer_thread.join() # Ensure thread completes before closing queue\n    await queue.aclose() # Crucial for proper shutdown of janus resources\n    print(\"Main: Queue closed\")\n\nif __name__ == '__main__':\n    asyncio.run(main())\n","lang":"python","description":"This example demonstrates how to use `janus.Queue` to allow a synchronous thread to put items into a queue while an asynchronous coroutine concurrently consumes them. It highlights the use of `.sync_q` and `.async_q` interfaces and the important `aclose()` call for cleanup."},"warnings":[{"fix":"Update `try...except RuntimeError` blocks to catch `janus.AsyncQueueShutDown` or `janus.SyncQueueShutDown` when dealing with queue shutdowns. Adjust code that previously relied on `task_done()` or `join()` raising exceptions after a queue is closed.","message":"In v2.0.0, the `shutdown()` method's error handling changed. Calling `shutdown()` on a closed queue now raises `janus.AsyncQueueShutDown` or `janus.SyncQueueShutDown` instead of `RuntimeError`. Additionally, `task_done()` and `join()` methods no longer raise exceptions on queue shutdown/closing, aligning with stdlib queue behavior. This may require updating exception handling logic. [cite: Release Notes]","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Upgrade your Python environment to 3.9 or a newer supported version.","message":"Version 1.1.0 dropped support for Python 3.7 and 3.8. The library now requires Python 3.9 or higher. [cite: Release Notes]","severity":"breaking","affected_versions":">=1.1.0"},{"fix":"Remove the `loop` argument when creating `janus.Queue` instances (e.g., `janus.Queue(loop=my_loop)` should become `janus.Queue()`). Ensure queue creation happens within an `asyncio` context where `asyncio.get_running_loop()` returns a valid loop.","message":"As of v0.5.0, explicit `loop` arguments were removed from `janus.Queue()` instantiation, and it became forbidden to create queues outside an active asyncio event loop. The library now automatically uses `asyncio.get_running_loop()`. [cite: Release Notes]","severity":"breaking","affected_versions":">=0.5.0"},{"fix":"Always include `await queue.aclose()` in your cleanup logic, typically at the end of your `async` entry point, to properly shut down internal resources.","message":"It is crucial to call `await queue.aclose()` when you are finished with a `janus.Queue`. Failure to do so can lead to `asyncio` generating error messages or resource leaks, as the library creates internal tasks to manage notifications.","severity":"gotcha","affected_versions":"All"},{"fix":"Evaluate your use case: if communication is strictly sync-to-sync or async-to-async, prefer Python's built-in `queue` or `asyncio.Queue` modules for better performance.","message":"janus queues are specifically designed for interoperation between synchronous threads and asynchronous asyncio tasks. For purely synchronous (thread-to-thread) or purely asynchronous (asyncio-to-asyncio) communication, using standard `queue.Queue` or `asyncio.Queue` respectively is recommended, as `janus` can introduce significant slowdowns in these single-mode scenarios.","severity":"gotcha","affected_versions":"All"},{"fix":"Ensure that `janus` queues are created and accessed within the context of a single asyncio event loop. For inter-process or inter-event loop communication, consider higher-level mechanisms like multiprocessing queues or message brokers.","message":"janus queues cannot be used for communication between two *different* asyncio event loops. Like other asyncio primitives, they are bound to the specific event loop in which they are created.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-11T00:00:00.000Z","next_check":"2026-07-10T00:00:00.000Z"}