.. _faq: ########################## Frequently Asked Questions ########################## Here are answers to some common questions about using Picodi. ***************************************************************************************************************************** Q: I'm injecting an ``async def`` dependency into a ``def`` function, but I get a coroutine object instead of the value. Why? ***************************************************************************************************************************** **A:** Synchronous functions (``def``) cannot ``await`` asynchronous operations. When you try to ``Provide`` an ``async def`` dependency into a sync function, Picodi cannot await the dependency provider to get its result within the synchronous context. Therefore, it injects the awaitable coroutine object itself. To correctly use the result of an async dependency, the function *consuming* it must also be ``async def``. The exception to this is when using :ref:`pre-initialized async dependencies with manual scopes `. If an async dependency using ``SingletonScope`` (or another manual scope) is initialized beforehand using ``await registry.init()``, its *cached value* can then be injected into synchronous functions because Picodi retrieves the already-computed value from the cache. *********************************************************** Q: When should I use ``SingletonScope`` vs. ``NullScope``? *********************************************************** **A:** * Use :class:`~picodi.SingletonScope` for dependencies that are: * **Expensive to create:** Like database connection pools, HTTP client sessions, or complex configuration objects. Creating them only once improves performance. * **Need to be shared globally:** When exactly one instance of a service or resource should be used throughout the application. * **Stateful (with caution):** If a dependency needs to maintain state across different parts of your application (use carefully to avoid unexpected side effects). Remember that ``SingletonScope`` requires manual cleanup via ``registry.shutdown()``. * Use :class:`~picodi.NullScope` (the default) for dependencies that are: * **Cheap to create:** Simple functions, small data objects. * **Stateless:** Dependencies whose instances don't carry state between calls. * **Need to be unique per use:** When each injection must receive a completely new instance. Cleanup happens automatically after the injection point finishes. **************************************************************** Q: Why do I need ``registry.shutdown()``? When should I call it? **************************************************************** **A:** ``registry.shutdown()`` is necessary to trigger the cleanup logic (the code after ``yield`` in generator dependencies) for dependencies managed by **manual scopes** (like ``SingletonScope`` and ``ContextVarScope``). Dependencies using ``AutoScope`` (like the default ``NullScope``) clean themselves up automatically after the injecting function finishes. Manual scopes, however, persist their instances until explicitly told to shut down. You should typically call ``registry.shutdown()`` **once** when your application is gracefully stopping. * For web applications, this might be during the ASGI application shutdown event. * For scripts or workers, it's usually at the very end of the main execution block. * The ``registry.lifespan()`` and ``registry.alifespan()`` context managers call ``shutdown()`` automatically upon exiting the context. If you have asynchronous cleanup logic (async yield dependencies in manual scopes), you **must** ``await registry.shutdown()``. *************************************** Q: Can I use Picodi without type hints? *************************************** **A:** Yes. Picodi's core injection mechanism relies on the ``Provide()`` marker in the default value, not on type hints. However, using type hints is **strongly recommended** for: * **Readability:** Makes it clear what type is expected or provided. * **Static Analysis:** Allows tools like MyPy to catch errors. * **Maintainability:** Makes the code easier to understand and refactor. **************************************************** Q: How does Picodi compare to FastAPI's built-in DI? **************************************************** **A:** FastAPI's DI is excellent and tightly integrated with route parameters, request data validation, and security. Picodi complements it rather than replacing it entirely. * **FastAPI DI excels at:** Handling request-specific data (path/query params, headers, bodies), security dependencies, and simple request-level dependencies. * **Picodi excels at:** * Managing application-level services and resources with controlled lifecycles (**scopes** like ``SingletonScope``). * Sharing dependencies between FastAPI routes and other parts of your application (workers, CLI commands). * Providing a consistent DI mechanism across different types of Python applications. * Advanced testing scenarios using flexible overrides. You can (and often should) use both together. Use FastAPI's ``Depends`` for request/route-level concerns and Picodi's ``Provide`` (especially ``Provide(..., wrap=True)``) for injecting application services or shared resources managed by Picodi. See :ref:`topics_integrations` for examples. ******************************************************************************* Q: My tests are failing because of state leaking between them. What's wrong? ******************************************************************************* **A:** This usually happens due to: 1. **Unmanaged Singletons:** If you use ``SingletonScope`` dependencies in tests but don't properly clean them up, their state persists across tests. 2. **Persistent Overrides:** If you apply overrides using the decorator (``@registry.override(...)``) or direct calls (``registry.override(...)``) but forget to clear them after the test. **Solutions:** * **Use the Picodi Pytest Plugin:** see :ref:`Pytest Integration ` for details. It automatically handles cleanup after each test, including calling ``shutdown()`` and clearing overrides. * **Manual Cleanup (if not using the plugin):** Ensure your test teardown logic (e.g., in ``pytest`` fixtures or ``tearDown`` methods) explicitly calls ``registry.shutdown()`` and ``registry.clear_overrides()``. * **Prefer Context Managers for Overrides:** Use ``with registry.override(...):`` within tests, as it automatically clears the override upon exiting the block. ************************************************************************************************************************************ Q: flake8-bugbear complains about ``B008 Do not perform function calls in argument defaults`` when using ``Provide``. How to fix it? ************************************************************************************************************************************ **A:** You need to tell ``flake8-bugbear`` that ``picodi.Provide`` is safe to use in defaults. Add or modify the ``extend-immutable-calls`` setting in your flake8 configuration file (e.g., ``setup.cfg``, ``tox.ini``, or ``.flake8``): .. code-block:: ini [flake8] # ... other settings ... extend-immutable-calls = picodi.Provide,Provide This informs the linter that ``Provide`` itself doesn't execute the dependency immediately but acts as a marker.