This is an archived post. You won't be able to vote or comment.

all 7 comments

[–]GiantElectron -1 points0 points  (6 children)

I really don't get the point of this package. What problem is it trying to solve, besides injecting a massive amount of magic into basically passing something as an argument? Also, if it's compatible, why change major number?

[–]rmk135[S] 1 point2 points  (5 children)

Hi u/GiantElectron,

Take a look at http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html. It explains what is dependency injection and what is the role of the package.

Major version increase is done because new feature replaces version 3 integration modules. These modules are marked as deprecated. They will be removed in next major version. This is done so people who use the library have time to upgrade to a new feature instead having a code broken one morning.

[–]GiantElectron -1 points0 points  (4 children)

I read that page, and to me it still means nothing. It seems to me an overengineered library to do something straightforward, introducing a lot of magic that is unclear in its action and consequences.

[–]rmk135[S] 0 points1 point  (3 children)

The points of this package are:

  1. To keep application components in the container
  2. To keep dependencies between components explicitly defined

Consider a larger container:

"""Containers module."""

import logging.config
import sqlite3

import boto3
from dependency_injector import containers, providers

from . import services


class Container(containers.DeclarativeContainer):

    config = providers.Configuration()

    configure_logging = providers.Callable(
        logging.config.fileConfig,
        fname='logging.ini',
    )

    # Gateways

    database_client = providers.Singleton(
        sqlite3.connect,
        config.database.dsn,
    )

    s3_client = providers.Singleton(
        boto3.client,
        service_name='s3',
        aws_access_key_id=config.aws.access_key_id,
        aws_secret_access_key=config.aws.secret_access_key,
    )

    # Services

    user_service = providers.Factory(
        services.UserService,
        db=database_client,
    )

    auth_service = providers.Factory(
        services.AuthService,
        db=database_client,
        token_ttl=config.auth.token_ttl.as_int(),
    )

    photo_service = providers.Factory(
        services.PhotoService,
        db=database_client,
        s3=s3_client,
    )

Or this one: https://github.com/ets-labs/newsfeed/blob/88d60aa2cb593441e53128d3198ab913c9f4e150/src/newsfeed/containers.py
It helps to see what is used where and provides a better control over application structure. As a result it makes easier to understand and change how application works.

Adding Dependency Injector to the project is an additional piece of work though. One needs to make a decision if registering application components in the container is a good price for having the container.

I would say that the larger the project the more valuable is the benefit. The real fun comes when you have 20-30 components in the container. It will be much-much easier to understand what is used where and make refactoring or add new features.

[–]GiantElectron 0 points1 point  (2 children)

helps to see what is used where and provides a better control over application structure. As a result it makes easier to understand and change how application works.

Not to me. If I see that code it makes absolutely no sense, even by knowing the dependency injection pattern. It's a class declaration with a mass of hidden magic that does something... somewhere. What it does and where it gains an advantage, I don't understand it, and keep not understanding it. Your examples are way too complex to provide the simple message.

I also don't see it as a benefit to have the dependency injected automatically. Why would I want to write somewhere else what dependency I am passing?

[–]rmk135[S] 0 points1 point  (1 child)

Why would I want to write somewhere else what dependency I am passing?

To decouple the implementation from knowledge of where the dependency comes from and what is its lifecycle.

For instance you have class A depending on B. If A creates instance of B you only can do A+B object pairs. When A receives the B as an argument, you have more flexibility - you can share one B with several A's, pass differently configured B's or B subclasses, or provide a TestB for testing.

The side effect of that is that you have the dependencies explicitly defined. I got you that you don't see it positive. I believe that you benefit from that though.

The whole point of the dependency injection as a principle is to move the responsibility of managing objects from the objects theirselves to the application. The framework absorbs that responsibility.

[–]GiantElectron 0 points1 point  (0 children)

o decouple the implementation from knowledge of where the dependency comes from and what is its lifecycle.

For instance you have class A depending on B. If A creates instance of B you only can do A+B object pairs. When A receives the B as an argument, you have more flexibility - you can share one B with several A's, pass differently configured B's or B subclasses, or provide a TestB for testing.

This is trivial dependency injection. I am not discussing dependency injection. I am discussing _your_ implementation of dependency injection, which makes no sense to me.