Injectable Properties

Fetch dependencies via properties rather than constructor arguments.

  1. Overview

Overview

This class allows you to specify dependencies by setting them as class properties instead of constructor arguments. This is common in clearskies as it helps make easily reusable classes - configuration can go in the constructor of the class, allowing the developer to directly instantiate it, and then the DI system will come by afterwards and provide the necessary dependencies.

After adding InjectableProperties as a parent of your class, you have two ways to specify your dependencies:

  1. By using the classes in the clearskies.di.inject.*module.
  2. By directly attaching objects which also use the InjectableProperties class.

The following table shows the dependencies that can be injected as properties via the clearskies.di.inject module:

ClassTypeResult
clearskies.di.inject.ByClassN/AThe specified class will be built
clearskies.di.inject.ByNameN/AThe specified dependnecy name will be built
clearskies.di.inject.CursorN/AThe PyMySQL cursor
clearskies.di.inject.DiN/AThe dependency injection container itself
clearskies.di.inject.Environmentclearskies.EnvironmentThe environment helper
clearskies.di.inject.InputOutputclearskies.input_outputs.InputOutputThe InputOutput object for the current request
clearskies.di.inject.Nowdatetime.datetimeThe current time (no timezone)
clearskies.di.inject.Requestsrequests.SessionA requests session
clearskies.di.inject.Utcnowdatetime.datetimeThe current time (tzinfo=datetime.timezone.utc)

Note: now/utcnow are not cached, so you’ll get the current time everytime you get a value out of the class property, unless a specific time has been set on the dependency injection container.

Here’s an example:

import clearskies
import time
import clearskies.decorators


class MyOtherThing(clearskies.di.InjectableProperties):
    now = clearskies.di.inject.Now()


class ReusableClass(clearskies.Configurable, clearskies.di.InjectableProperties):
    my_int = clearskies.configs.Integer(required=True)
    some_number = clearskies.di.inject.ByName("some_number")
    my_other_thing = clearskies.di.inject.ByClass(MyOtherThing)

    @clearskies.decorators.parameters_to_properties
    def __init__(self, my_int: int):
        self.finalize_and_validate_configuration()

    def my_value(self) -> int:
        return self.my_int * self.some_number


class MyClass(clearskies.di.InjectableProperties):
    reusable = ReusableClass(5)


class MyOtherClass(clearskies.di.InjectableProperties):
    reusable = ReusableClass(10)


di = clearskies.di.Di(
    bindings={
        "some_number": 10,
    }
)

my_class = di.build(MyClass)
print(my_class.reusable.my_value())  # prints 50

my_other_class = di.build(MyOtherClass)
print(my_other_class.reusable.my_value())  # prints 100

start = my_class.reusable.my_other_thing.now
time.sleep(1)
stop = my_class.reusable.my_other_thing.now
print((stop - start).seconds)  # prints 1