PythonNest / PyNest

PyNest is a Python framework built on top of FastAPI that follows the modular architecture of NestJS
https://pythonnest.github.io/PyNest
MIT License
643 stars 45 forks source link

fix: return class in injectable decorator #59

Closed Ignacio-Freire closed 1 month ago

Ignacio-Freire commented 1 month ago

Proposal

Linked to issue https://github.com/PythonNest/PyNest/issues/58

The issue with the Injectable decorator returning a string instead of the class itself likely stems from the decorator definition.

In the definition of Injectable, cls is treated as if it could potentially be a string (cls: str = None), which suggests that the decorator might be designed to accept optional parameters or configurations. However, if not handled properly, this design could lead to unexpected behavior, such as treating a class definition as a string.

Correcting the Injectable Decorator

The decorator needs to correctly handle both cases: when it is used with and without parameters. Here’s a more standard way of creating a decorator that can optionally accept arguments

Explanation

  1. Decorator Factory: The Injectable function can now correctly handle being called either as @Injectable or @Injectable(). It uses a nested decorator function to apply the actual class modifications.

  2. Class Modifications: It checks if the class has an init method defined and, if not, assigns a default one. It then parses dependencies, sets necessary attributes, and applies injection.

  3. Handling Arguments: The outer function (Injectable) checks if it is given a class directly (cls is not None). If so, it directly returns the decorator applied to the class. Otherwise, it returns the decorator function itself, allowing for further customization or arguments.

Tests results

❯ pytest
============================================ test session starts ============================================
platform linux -- Python 3.11.7, pytest-8.2.0, pluggy-1.5.0
rootdir: /projects/personal/PyNest
configfile: pyproject.toml
plugins: anyio-4.3.0
collected 20 items

tests/test_core/test_database/test_odm.py ...                                                         [ 15%]
tests/test_core/test_database/test_orm.py ....                                                        [ 35%]
tests/test_core/test_decorators/test_controller.py .....                                              [ 60%]
tests/test_core/test_pynest_application.py ...                                                        [ 75%]
tests/test_core/test_pynest_container.py ..                                                           [ 85%]
tests/test_core/test_pynest_factory.py ...                                                            [100%]

============================================= warnings summary ==============================================
tests/test_core/test_decorators/test_controller.py:6
  cannot collect test class 'TestController' because it has a __init__ constructor (from: tests/test_core/test_decorators/test_controller.py)
    @Controller(prefix="api/v1/user", tag="test")

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================================= 20 passed, 1 warning in 0.26s =======================================