Skip to main content

Simple dependency injection framework for python for easy and logical app configuration.

Project description

config-injector

Config-injector is a very simple framework which aims to do only two things: (1) define configurable functions and (2) inject configuration data into those functions at runtime.

Installation

Install with pip.

pip install config-injector

Getting Started

Annotate any callable as a configurable function using @config. Note that the @config decorator requires that you provide callable functions for each argument. These callable functions should return the expected type. The object is to break all arguments down to fundamental types: string, integer, float or dictionary.

from collections import namedtuple
from typing import Text, Dict, SupportsInt
from pathlib import Path

from config_injector import config, Injector


MockThing0 = namedtuple("MockThing0", ["arg_1", "arg_2", "arg_3", "arg_4"])

@config(arg_1=str, arg_2=str, arg_3=str, arg_4=str)
def mock_thing_0(arg_1: Text, arg_2: Text, arg_3: Text, arg_4: Text):
    return MockThing0(arg_1, arg_2, arg_3, arg_4)


@config(arg_5=int, arg_6=int, arg_7=int, arg_8=int)
def mock_thing_1(arg_5, arg_6, arg_7, arg_8):
    return {"key_a": arg_5, "key_b": arg_6, "key_c": arg_7, "key_d": arg_8}

@config(t0=mock_thing_0, t1=mock_thing_1, arg_9=str)
def mock_things(t0: MockThing0, t1: Dict[SupportsInt], arg_9: Text):
    return (t0, t1, arg_9)

def get_things(config_file=Path("config.json")):
    injector = Injector()
    injector.load_file(config_file)
    return injector["things"].instantiate(mock_things)

Now that the configurable functions are annotated, we can write a configuration for them.

{
  "things": {
    "t0": {"arg_1": "a", "arg_2": "b", "arg_3": "c", "arg_4": "d"},
    "t1": {"arg_5": 1, "arg_6": 2, "arg_7": 3, "arg_8": 4},
    "arg_9": "e"
  }
}

This configuration file can be loaded in the runtime portion of our implementation using get_things() to instantiate the configured objects created by our functions.

Polymorphism

It is common to want to determine the implementation at runtime. This can be accomplished by delaring the class of an argument as a tuple of multiple types.

from config_injector import config, Injector

class BaseClass:...

class ImplementationA(BaseClass):...

class ImplementationB(BaseClass):...

@config()
def implementation_a():
    return ImplementationA()

@config()
def implementation_b():
    return ImplementationB()

@config(t0=(implementation_a, implementation_b))
def mock_thing(t0):
    return {
        "t0": t0
    }

# Instantiate using implementation a.
mock_thing_using_a = Injector({"t0": {"type": "implementation_a"}}).instantiate(mock_thing)
# Instantiate using implementation b.
mock_thing_using_b = Injector({"t0": {"type": "implementation_b"}}).instantiate(mock_thing)

Environment Variable Interpolation

Configurations can contain environment variables for any value. Variables shall be placed within braces ${VAR_NAME} and use only letters and underscores. For example, for the following configuration, the environment variables would be interpolated.

{
    "db": {
         "url": "${DB_URL}",
         "user": "${DB_USER}",
         "password": "${DB_PASSWORD}",
    }
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

config-injector-0.3.1.tar.gz (6.4 kB view hashes)

Uploaded Source

Built Distribution

config_injector-0.3.1-py3-none-any.whl (6.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page