Code Monkey home page Code Monkey logo

easy_ioc's Introduction

EASY-IOC


███████╗ █████╗ ███████╗██╗   ██╗     ██╗ ██████╗  ██████╗
██╔════╝██╔══██╗██╔════╝╚██╗ ██╔╝     ██║██╔═══██╗██╔════╝
█████╗  ███████║███████╗ ╚████╔╝█████╗██║██║   ██║██║     
██╔══╝  ██╔══██║╚════██║  ╚██╔╝ ╚════╝██║██║   ██║██║     
███████╗██║  ██║███████║   ██║        ██║╚██████╔╝╚██████╗
╚══════╝╚═╝  ╚═╝╚══════╝   ╚═╝        ╚═╝ ╚═════╝  ╚═════╝

a simple dependency-injection python library, which implemented with pure python and meta-programming. It is non-invasive and progressive to refactor your code without learning complex concepts.

Install

pip install easy-ioc

FAQ

Why I need dependency-injection?

  1. See the example below. Basically, you always need to manually pass dependencies of class to the constructor of class. And constructor should set instance attribute from parameters. It wastes lots of time and coupling instances each other.
  2. lazy-compute defer initialization of instance after dependencies injected. Therefore, constructor just focus on initialization.

How it works?

Meta-programming is key of how to make a class. easy-ioc use meta-programming to save the dependencies of class inside. It cost little performance to achieve. When you try to figure out the map of your project, easy-ioc can generate the flat tree of dependencies. Then you can inject all dependencies you need.

Can I use in Python 3.5 lower?

If you're still using 2.7. Pull origin and checkout branch python2.7. And for python version greater 3 but lower 3.5, you just need to remove typing in easy_ioc.inject.

Why all dependencies save as class attributes, all instance share same dependencies?

the inject function create an Injectable instance which help you access own dependencies correctly. Different instance share same dependencies map, but access own dependencies privately.

Is it easy to be tested?

It is not just easy to be tested. It makes testing much easier. Think about use case as below

  1. a mono-repo application, need connecting db or loading config from file.
  2. an async application, an async object dependent on other async object.

Example

project without easy_ioc

# example/todo_without_ioc.py
import random
import logging

class DB(object):
    def __init__(self,url):
        pass
    def find(self, index):
        return self

    def delete(self, index):
        return self

    def update(self, index):
        return self

    def get(self, index):
        return self

class SQLite(DB):
    def __init__(self, url):
        super(SQLite,self).__init__(url)
        logging.info("using SQLite with {}".format(url))

class Mongo(DB):
    def __init__(self,url):
        super(Mongo, self).__init__(url)
        logging.info("using MongoDB with {}".format(url))



class TokenLibrary(object):
    def __init__(self, use_openssl):
        logging.info('{}using openssl '.format('not ' if not use_openssl else ''))

    def generate(self, index, expire_time):
        return b''.join([chr(random.randint(65, 90)).encode() for i in range(64)])


class LoginModule(object):
    def __init__(self, db, token_library):
        self.db = db
        self.token_library = token_library
        logging.info("using {} and {}".format(db, token_library))

    def get_token(self, user_id, password):
        if self.db.find(user_id, password=password):
            return self.token_library.generate(user_id, expire_time=7200)


class TodoModule(object):
    def __init__(self, db):
        self.db = db
        logging.info("using {}".format(db))

    def add(self, todo):
        self.db.add(todo)

    def delete(self, todo):
        self.db.find(todo).delete()

    def update(self, todo):
        self.db.find(todo.id).update(todo)

    def get(self, user):
        return self.db.find(user=user.id)


class TodoService(object):
    def __init__(self, todo_module, login_module, vip_login_module):
        self.todo_module = todo_module
        self.login_module = login_module
        self.vip_login_module = vip_login_module
        logging.info("todo_module:{}".format(todo_module))
        logging.info("login_module:{}".format(login_module))
        logging.info("login_module:{}".format(vip_login_module))
        super(TodoService, self).__init__()


def new_todo_service():
    user_db = Mongo('https://user.mongo.local:27017'),
    todo_db = SQLite(':memory:')
    token_library = TokenLibrary(False)
    openssl_token_library = TokenLibrary(True)
    login_module = LoginModule(user_db, token_library)
    vip_login_module = LoginModule(user_db, openssl_token_library)
    todo_module = TodoModule(todo_db)
    return TodoService(todo_module, login_module,vip_login_module)


if __name__ == '__main__':
    logging.getLogger().setLevel(logging.INFO)
    new_todo_service()

refactor project with easy_ioc

# example/todo_with_ioc.py
from example.todo_without_ioc import SQLite, TodoModule, TodoService, LoginModule, TokenLibrary,DB
from easy_ioc import Container, inject
from example.dependencies import dependencies
import logging
import io


class LoginModuleContainer(LoginModule, Container):
    user_db = inject(DB) # using DB, the parent class as interface
    token_library = inject(TokenLibrary)

    def __init__(self):
        super(LoginModuleContainer, self).__init__(self.user_db, self.token_library)


class TodoModuleContainer(TodoModule, Container):
    todo_db = inject(DB) # using DB, the parent class as interface

    def __init__(self):
        super(TodoModuleContainer, self).__init__(self.todo_db)


class TodoServiceContainer(TodoService, Container):
    todo_module = inject(TodoModuleContainer)  # type: TodoModuleContainer
    vip_login_module = inject(LoginModuleContainer)  # type: LoginModuleContainer
    login_module = inject(LoginModuleContainer)  # type: LoginModuleContainer

    def __init__(self, host, port):
        self.host = host
        self.port = port
        # If you're using python doesn't support typing
        # you can add comment like upper to help ide or editor to know what type should be
        # or assert with isinstance(attr,type), and it won't raise AssertionError.
        # Because it raises an DependencyError before assert isinstance
        # this library also can use in python2.7(not tested under lower version)
        # it can use to refactor project too old too coupled by each module
        super(TodoServiceContainer, self).__init__(self.todo_module, self.login_module)


def new_todo_service():
    file = io.StringIO()
    TodoServiceContainer.generate(file)
    file.seek(0)
    print(file.read())
    # what file read should be as below
    # dependencies = {
    #     "container": {
    #         "TodoServiceContainer": {
    #             "host": None,
    #             "port": None
    #         },
    #         "TodoServiceContainer.todo_module": {},
    #         "TodoServiceContainer.vip_login_module": {},
    #         "TodoServiceContainer.login_module": {}
    #     },
    #     "container_dependencies": {
    #         "TodoServiceContainer.todo_module.todo_db": None,
    #         "TodoServiceContainer.vip_login_module.user_db": None,
    #         "TodoServiceContainer.vip_login_module.token_library": None,
    #         "TodoServiceContainer.login_module.user_db": None,
    #         "TodoServiceContainer.login_module.token_library": None
    #     }
    # }

    # I updated the dependencies as example/dependencies.py like below
    # from example.todo_without_ioc import SQLite, TokenLibrary, Mongo
    # import logging
    # logging.getLogger().setLevel(logging.INFO)
    # todo_db = Mongo('https://user.mongo.local:27017')
    # vip_db = Mongo('https://vip-user.mongo.local:27017')
    # user_db = SQLite(':memory:')
    # token_library = TokenLibrary(use_openssl=False)
    # openssl_token_library = TokenLibrary(use_openssl=True)
    # host = "localhost"
    # port = 8080
    # dictionary of children of container is the keyword arguments of class consturtor
    # value of container_dependencies is the instance dependencies
    # dependencies = {
    #     "container": {
    #         "TodoServiceContainer": {
    #             "host": host,
    #             "port": port
    #         },
    #         "TodoServiceContainer.todo_module": {},
    #         "TodoServiceContainer.login_module": {},
    #         "TodoServiceContainer.vip_login_module": {}
    #     },
    #     "container_dependencies": {
    #         "TodoServiceContainer.todo_module.todo_db": todo_db,
    #         "TodoServiceContainer.login_module.user_db": user_db,
    #         "TodoServiceContainer.login_module.token_library": token_library,
    #         "TodoServiceContainer.vip_login_module.user_db": vip_db,
    #         "TodoServiceContainer.vip_login_module.token_library": openssl_token_library
    #     }
    # }

    # let's inject the dependencies
    return TodoServiceContainer.inject(dependencies)

if __name__ == '__main__':
    logging.getLogger().setLevel(logging.INFO)
    # all dependencies were injected into service
    service = new_todo_service()
   

easy_ioc's People

Contributors

bingoxuan avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

Forkers

simon0729

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.