Thursday, March 2, 2023

Python: importing settings or dependency injection

Python: importing settings or dependency injection

Two common approaches to configuring a python application are importing settings and dependency injection.

Importing settings involves creating a settings module or file that contains all the values and configurations needed for your application to run. These settings can then be imported into other modules and used directly. The advantage of this approach is that it is simple and easy to understand, and can work for small projects. The disadvantage is that it increases coupling to the settings module and to the way that those settings are collected. It also makes it harder to unit test a module or class without having to resort to mocking the settings module in some way.

Dependency injection involves passing dependencies (such as settings) into a class as parameters, typically using a class method that sets some class variables.

The advantage of this is that the class needs no knowledge of the settings module or how those settings are loaded. It also makes testing easier because tests can either rely on the classes default settings or inject their own test settings. The class can also be re-used more easily.

Compare these two examples:

Importing Settings

settings.py

# Some settings as class variables.. maybe use pydantic BaseSettings
class Settings():
    some_setting = "some_value"

some_class.py

from settings import Settings

class SomeClass():
    SOME_SETTING = Settings.some_setting

    def __init__(self):
        print(f"SOME_SETTING = {self.SOME_SETTING}")

Dependency Injection

settings.py

# Some settings as class variables.. maybe use pydantic BaseSettings
class Settings():
    some_setting = "some_value"

    # Inject the settings into the SomeClass class
    from some_class import SomeClass
    SomeClass.configure(some_setting=Settings.some_setting

some_class.py

class SomeClass():
    SOME_SETTING = "default"

    @classmethod
    def configure(some_setting):
        SOME_SETTING = some_setting

    def __init__(self):
        print(f"SOME_SETTING = {self.SOME_SETTING}")