Metadata-Version: 2.1
Name: quickd
Version: 0.1.1
Summary: Decorator type based dependency injection
Home-page: https://github.com/vimtor/quickd
Author: Victor Navarro
Author-email: victor@vimtor.io
License: MIT
Project-URL: Bug Tracker, https://github.com/vimtor/quickd/issues
Description: <div align="center">
           <h1 align="center">quickd</h1>
           <p align="center">Decorator type-based dependency injection for Python</p>
           <p align="center">
            <img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/vimtor/quickd/Test">
            <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/vimtor/quickd">
            <img alt="GitHub" src="https://img.shields.io/github/license/vimtor/quickd">
            <img alt="PyPI - Downloads" src="https://img.shields.io/pypi/dw/quickd">
           </p>
        </div>
        
        ## 📦 Installation
        
        The package `quickd` supports Python >= 3.5. You can install it by doing:
        
        ```shell
        $ pip install quickd
        ```
        
        ## 📜 Example
        
        Here is a quick example:
        
        ```python
        from quickd import inject, factory
        
        
        class Database:
            pass
        
        
        class PostgreSQL(Database):
            def __str__(self):
                return 'PostgreSQL'
        
        
        class MySQL(Database):
            def __str__(self):
                return 'MySQL'
        
        
        @inject
        def print_database(database: Database):
            return print(database)
        
        
        @factory
        def choose_database() -> Database:
            return PostgreSQL()
        
        
        print_database()  # Prints: PostgreSQL
        print_database(MySQL())  # Prints: MySQL
        ```
        
        ## 🚀 Usage
        
        There are only 3 decorators that compose the whole framework
        
        ### `@factory`
        
        - Registers an instance for a specific type for later use with `@inject`
        - Is mandatory to annotate the function with the return type of the class that you want to inject later
        - It is **not** dynamic, so the implementation can only be chosen once
        
        ```python
        from quickd import factory
        
        
        @factory
        def choose_database() -> Database:
            return PostgreSQL()
        ```
        
        ### `@inject`
        
        - Injects dependencies to a function by matching its arguments types with what has been registered
        - As you can see below, it also works with constructors
        
        ```python
        from quickd import inject
        
        
        @inject
        def print_database(database: Database):
            return print(database)
        
        
        class UserService:
            @inject
            def __init__(self, database: Database): pass
        ```
        
        ### `@service`
        
        - Registers a class to be later injectable without using `@factory`
        - It also applies `@inject` to its constructor
        
        ```python
        from quickd import service, inject
        
        
        @service
        class UserService:
            def __init__(self):
                self.users = ['Bob', 'Tom']
        
            def all(self):
                return self.users
        
            def add(self, user):
                self.users.append(user)
        
        
        @inject
        def get_users(service: UserService):
            return service.all()
        
        
        @inject
        def add_user(service: UserService):
            return service.add("Pol")
        
        
        get_users()  # ['Bob', 'Tom']
        add_user()
        get_users()  # ['Bob', 'Tom', 'Pol']
        ```
        
        ## 👨‍🍳 Recipes
        
        Here are some common solutions to scenarios you will face.
        
        ### Interfaces
        
        ```python
        from abc import abstractmethod
        from quickd import inject, factory
        
        
        class UserRepository:
            @abstractmethod
            def save(self, user):
                pass
        
            @abstractmethod
            def search(self, id):
                pass
        
        
        class UserCreator:
            @inject
            def __int__(self, repository: UserRepository):
                self.repository = repository
        
            def create(self, user):
                self.repository.save(user)
        
        
        class MySQLUserRepository(UserRepository):
            def __int__(self, host, password):
                self.sql = MySQLConnection(host, password)
        
            def save(self, user):
                self.sql.execute('INSERT ...')
        
            def search(self, id):
                self.sql.execute('SELECT ...')
        
        
        @factory
        def choose_user_repository() -> UserRepository:  # Notice super class is being used
            return MySQLUserRepository('user', '123')
        ```
        
        ### Testing
        
        Following the above example we can create a unit test mocking the persistance, which will make our tests easier and
        faster.
        
        ```python
        fake_user = {'id': 1, 'name': 'Tom'}
        
        
        class FakeUserRepository(UserRepository):
            def save(self, user):
                assert user == fake_user
        
        
        repository = FakeUserRepository()
        user_creator = UserCreator(repository)
        
        user_creator.create(fake_user)
        ```
        
        ### Configuration
        
        There are multiple ways to configure your classes. A simple approach is to use environment variables on your factory
        annotated methods.
        
        ```python
        import os
        from quickd import factory
        
        
        @factory
        def choose_database() -> Database:
            username = os.environ.get("POSTGRES_USER")
            password = os.environ.get("POSTGRES_PASS")
            return PostgreSQL(username, password)
        ```
        
        ## 🧠 Motivation
        
        Dependency injection provides a great way to decouple your classes in order to improve testability and maintainability.
        
        Frameworks like [Spring](https://spring.io/) or [Symfony](https://symfony.com/) are loved by the community.
        
        > I will just add a parameter to the constructor and Spring will fill with a global instance of the class
        
        These frameworks rely heavy on the type system, to know which class should go where.
        
        From Python 3.5 we have the [typing](https://docs.python.org/3/library/typing.html) package. This addition allows us to
        have the dependency injection framework that Python deserves.
        
        
Keywords: dependency injection,unif of work,decorators,typings
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Testing
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Requires-Python: >=3.5
Description-Content-Type: text/markdown
Provides-Extra: tests
Provides-Extra: build
