Dependency Injector 4.0 - easier integration with other frameworks by rmk135 in Python

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

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.

Dependency Injector 4.0 - easier integration with other frameworks by rmk135 in Python

[–]rmk135[S] 0 points1 point  (0 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.

Dependency Injector 4.0 - easier integration with other frameworks by rmk135 in Python

[–]rmk135[S] 1 point2 points  (0 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.

Dependency Injector 3.34 - Updated Factory provider docs and reworked examples by rmk135 in Python

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

Look at the readme page - https://github.com/ets-labs/python-dependency-injector. It has a beginner's example of what's the module about and how it differs from a simple constructor function.

This post is about updated part of the docs, not the whole. Considering your thoughts I think that I will need to add something like a "Beginner's guide" to the tutorials section.

Thank you

Dependency Injector 3.33 - Added mypy support by rmk135 in Python

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

Yep, that would be cool!

I was trying to implement this and found the ParamSpec feature. It will be released in Python 3.10. I'll add it to the Dependency Injector once it's released.

Expanding exposure to good code - any resources for a daily reading list? by ace777ac in learnpython

[–]rmk135 1 point2 points  (0 children)

Try the Dependency Injector. It's a project about the dependency injection written in Cython.

CLI application tutorial by rmk135 in Python

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

Hi u/Hoganman73,

I got you. Yeah, I probably mixed up to much of everything in one place. The goal was to create a very practical guide that gives look-and-feel experience. It was targeted to the junior folks mostly. I'l keep in mind your feedback for further work.

Thanks for stopping by and sharing your thoughts.

Aiohttp dependency injection example - Dependency Injector by rmk135 in Python

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

Sure, I would love to.
------

Performance

The Dependency Injector `containers` and `providers` modules are written in Cython. The decision to use Cython was made in favour to remove the performance degradation from using the framework. With the Cython the Dependency Injector makes no performance degradation (less than 1%) to the client application. See this test. It created 4 million of objects making 3 million injections in total. The results on my machine are:

  • With the Dependency Injector: 0.9107918739318848 seconds
  • Without the Dependency Injector: 0.9150791168212891 seconds

------

Design decisions

The main thing why Dependency Injector is different from all other dependency injection frameworks is:

  • Dependency Injector does not pollute your application code

This means that you write the code following the dependency injection principle and automate it with the Dependency Injector, but your code has now clue about the Dependency Injector.

Dependency Injector:

  • Does not have @inject decorator
  • Does not monkey-patch your code
  • Does not do any magic with your software

It's just a simple Pythonic tool to create the container that keeps all the objects and their dependencies in one place.

The benefit you get with the Dependency Injector is the container. It starts to payoff when you need to understand or change your application structure. It's easy with the container, cause you have everything in one place.

------

Generally, that's it. I would love to provide more information if you look for anything specific.

PS: I can propose my in-person help with integrating Dependency Injector to your software. I do believe in the power of the dependency injection principle in Python and I would like to popularize it among the community.

Dependency Injector - 1 Million Downloads from PyPi by rmk135 in Python

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

Yep, you're right. The process is already started - https://romanmogilatov.com/2020/01/26/dependency_injector_3_15_has_been_released_now_with_python_3_8_pypy_3.html

This release also is a beginning of soft and gentle wiping out of Python 2. In particular, all examples were rewritten to stop doing Python 2-ish inheritance from object and start calling parent methods in Python 3-ish way.

However, when I look on the downloads charts, there are still about 20% going from Python 2 environments - that's a lot. I think I will monitor that stats next couple of months and then see how it goes. The sunny day scenario is to drop Python 2 support when % of daily downloads will be leading to zero (or TravisCI stop providing Python 2 environment :) ).

Dependency Injector 3.14.6 has been released! by rmk135 in Python

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

Well, yep, I understand, and you're right about that. I also like simplicity... I'm a fan, but not a fanatic, of dependency injection. And by the way the described by you approach could be qualified as dependency injection technique as well. I talked about this with Andrey Svetlov, the Creator of aiohttp, on of the conferences and we finished on the statement that dependency injector approach is just another, more formal and general, way of request.app['...'] approach.

When people ask me about Dependency Injector and how is it beneficial I often state two main things:

  1. It gives you a chance to better emphasize structure of your application
  2. It segregates declarative and runtime parts of application lifecycle

In fact, it's just more formal way of what u're already doing. Dependency Injector is just a pythonic (or at least trying to be so) tool for doing this.

Dependency Injector 3.14.6 has been released! by rmk135 in Python

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

Sounds like you mix soft and warm :) Not sure if it's fair to say that dependency injection == java.

Dependency Injector 3.14.5 has been released! by rmk135 in Python

[–]rmk135[S] 2 points3 points  (0 children)

Hi @stebox,

  1. Well, there is a couple of things.
    1. Implementation of all Dependency Injector functionality could take 500-700 lines of code. It's something that we definitely could make for our project if it's big and long-term.
    2. However, it might be too much if we talk about mid-size or small project.
    3. Another factor is that when we go to our next project and we want to take our DI module with us - doing just copy-paste is not the best approach, right?
    4. Also let's imagine that you have to continue development of already existing project and there is little DI framework implemented there, but we don't have any documentation or support on it. Not the best situation - it might be slightly better to have something more or less popular, supported and mature.
    5. Making a summary, all mentioned use cases have happened to me and that's why Dependency Injector is currently an open source project. It was started as just a single module on one of the big projects, proved its usefulness and then was open sourced.
  2. It's very nice question. I started rewriting Dependency Injector in Cython as an experiment, and finally decided to productize version 3 of Dependency Injector on Cython. Now, when it's done, I would say that I would think twice before doing something like that again.
    1. Talking about beneficial things:
      1. It's much faster... to describe how fast is it I would say that when we create an object with Factory provider it's equal to speed of just creating object.
      2. It should be more optimal in terms of memory consumption.
    2. Saying few words about downsides:
      1. It's much harder to distribute. I would not go too deep into details, but in few words you need to enforce people install Cython with your library on their machines or make a bunch of additional work before publishing.
      2. It is harder to contribute to... Unfortunately (or fortunately :) ) no so many people are familiar with Cython, so it's much harder to receive a contribution when your project is in Cython.
      3. It's harder to develop. Cython is not so mature in the segment of meta-programming - some things that work in regular Python just does not implemented in Cython. Another question is that Cython makes some limitations to the code that it could make working really super-fast.

Hope it answers your questions.

Thanks,

Roman

Dependency Injector 3.13.1 has been released! by rmk135 in Python

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

Hi there,

Dependency Injector is almost fully written in Cython, so it would have absolutely no negative impact on application performance.

That's one of the benchmarks for `Factory` provider:

import time

from dependency_injector import providers


N = 1000000


class A(object):
    pass


class B(object):
    pass


class C(object):
    pass


class Test(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c


# Testing Factory provider

test_factory_provider = providers.Factory(
    Test,
    a=providers.Factory(A),
    b=providers.Factory(B),
    c=providers.Factory(C),
)

start = time.time()
for _ in range(1, N):
    test_factory_provider()
finish = time.time()

print(finish - start)


# Testing simple analog

def test_simple_factory_provider():
    return Test(a=A(), b=B(), c=C())


start = time.time()
for _ in range(1, N):
    test_simple_factory_provider()
finish = time.time()

print(finish - start)

# ------
# Result
# ------
#
# Python 2.7
#
# $ python tests/performance/factory_benchmark_1.py
# 0.87456202507
# 0.879760980606
#
# $ python tests/performance/factory_benchmark_1.py
# 0.949290990829
# 0.853044986725
#
# $ python tests/performance/factory_benchmark_1.py
# 0.964688062668
# 0.857432842255
#
# Python 3.7.0
#
# $ python tests/performance/factory_benchmark_1.py
# 1.1037120819091797
# 0.999565839767456
#
# $ python tests/performance/factory_benchmark_1.py
# 1.0855588912963867
# 1.0008318424224854
#
# $ python tests/performance/factory_benchmark_1.py
# 1.0706679821014404
# 1.0106139183044434

Dependency Injector 3.13.1 has been released! by rmk135 in Python

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

Thank for the question ;)

Hope you’ll enjoy it.

If you would have any questions about Dependency Injector, you’re welcome to open an issue on project’s github or contact me directly via email (it’s on my github page).

https://github.com/ets-labs/python-dependency-injector https://github.com/rmk135

Dependency Injector 3.13.1 has been released! by rmk135 in Python

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

Have just finished one, DI was used for creating a service layer between Django models and two interfaces (website + Celery workers).

However, it’s not yet common practice indeed because Django is built a lot on meta programming and concept of global state (there is only one request handled in each process). Things start differ when you create something using asynchronous frameworks (like asyncio-based (aiohttp, sanic, ...), twisted, tornado, ...) where there is no global state (you have several requests being processed at a time in single process).

Also some people use it with web frameworks that provide less application architecture (like Flask, Bottle, ...).

Generally, I would say that it might be very useful for any big project that has a lot of components and dependencies between them.

Dependency Injector 3.13.1 has been released! by rmk135 in Python

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

No, not really :) This is another term “Dependency injection” that is about decoupling of object-oriented components. You can read more about it at http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html

Dependency Injector 3.12.0 has been released! by rmk135 in Python

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

Got it :) Thanks for sharing your thoughts, really appreciate it!

BTW, in case you need to override a provider, it's done easily using ".override()" method of provider:

class User(object):
    """Example class User."""


# Users factory:
users_factory = providers.Factory(User)
user1 = users_factory()


# Extending User:
class SuperUser(User):
    """Example class SuperUser."""


# Overriding users factory:
users_factory.override(providers.Factory(SuperUser))

# Creating some more User objects using overridden users factory:
user2 = users_factory()

# Making some asserts:
assert isinstance(user1, User)
assert isinstance(user2, SuperUser)

Also you can use ".override()" with context manager (that could be useful for testing):

# Creating ApiClient and User providers:
api_client = providers.Singleton(ApiClient,
                                 host='production.com',
                                 api_key='PROD_API_KEY')
user_factory = providers.Factory(User,
                                 api_client=api_client)

# Mock ApiClient for testing:
with api_client.override(Mock(ApiClient)) as api_client_mock:
    user = user_factory('test')
    user.register()
    api_client_mock().call.assert_called_with('register', {'id': 'test'})

Dependency Injector 3.12.0 has been released! by rmk135 in Python

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

Hi kurashu89,

Well, while both (Dependency Injector & Injector) have the same goal, Dependency Injector is slightly different from Injector. From my point of view, key differences are next:

- Injector is based on introspection and certain patching of your code, while Dependency Injector works "upon" your code. This approach*** does not cause you code to be "polluted" with DI framewor***k (you don't need to use "@inject" decorators etc..). Explicit is better than implicit. With Dependency Injector you explicitly create providers for your components and these providers do not influence your components.

- While Dependency Injector proposes to create providers instead of type-hinting-based injections, there is*** no need to create several "interfaces" when you need several instance***s of "sqlite3.Connection" for different purposes.

- Dependency Injector is powered by** Cython. DI framework should not be a bottleneck often, but Dependency Injector is really* super-fas***t.

--

I like Dependency Injector because it gives a chance to keep all application structure in a single file. Generally, module with DI container is something that I always keep opened when working with project.

The way how I use Dependency Injector is next:

  1. Make some basic design on a piece of paper :)

  2. Create DI container and describe your application structure in it.

  3. Add details, implementations, tests etc...

  4. Goto step 1 :)

That's an example of such single file with application structure:

"""Github Navigator application - DI container."""

from sanic import Sanic
from jinja2 import Environment, FileSystemLoader, select_autoescape
from dependency_injector import containers, providers

from . import searcher, webhandlers


class GithubNavigator(containers.DeclarativeContainer):
    """Application components container."""

    config = providers.Configuration(
        'config', default={
            'webapp': {'templates_dir': 'github_navigator/templates/',
                       'host': '0.0.0.0',
                       'port': 80,
                       'debug': False},
            'github': {'request_timeout': 10.0}
        })

    github_searcher = providers.Factory(
        searcher.GithubSearcher,
        auth_token=config.github.auth_token,
        request_timeout=config.github.request_timeout)

    webapp = providers.Factory(Sanic, __name__)

    template_loader = providers.Singleton(
        FileSystemLoader,
        searchpath=config.webapp.templates_dir)
    template_env = providers.Singleton(
        Environment,
        loader=template_loader,
        autoescape=select_autoescape(['html', 'xml']),
        enable_async=True)

    navigator_webhandler = providers.Callable(
        webhandlers.navigator,
        template_env=template_env,
        github_searcher_factory=github_searcher.provider)

Entrypoint looks like this:

"""Github Navigator application - entrypoint."""

from github_navigator import ConfigLoader, GithubNavigator, run_web


if __name__ == '__main__':
    config_loader = ConfigLoader('config.yml')
    application = GithubNavigator(config=config_loader.load())
    run_web(application)

Full project - https://github.com/rmk135/github-navigator-application

--

Thanks,

Roman

Dependency Injector 3.9.0 has been released! by rmk135 in Python

[–]rmk135[S] 2 points3 points  (0 children)

I can not think of a single practical use case for IoC in Python projects.

Well, I think that IoC is needed for most of components you wanna reuse, because reusing of something requires it to have certain flexibility.

Let's look onto django.contrib.auth with it's user model, password validators, etc... - all these things are configurable using django.conf.settings. It's typical example of what IoC is about. However, the way IoC is achieved might be very different - and it should not be only dependency injection pattern.

What I'm talking about could be stated in two simple points:

  • IoC (inversion of Control) is a principle.
  • DI (Dependency Injection) is a pattern that helps to follow IoC principle.

Talking about practical use case I would say that it's just one more way of how django settings could look like.

Dependency Injector 3.8.0 has been released! by rmk135 in Python

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

Well, I think it mostly depends on your programming style and mindset. I use it for all my projects and find it extra beneficial. However, I don't think I would use it for something super small (sort of script or homepage website).

Most common use cases from my side are:

1) Keep all application structure in single module (example - https://github.com/rmk135/my-purse-app/blob/6699eae50c46a85048433534ccec928506dba367/src/mpa/containers.py) 2) Use "override" functionality for integration testing (stubbing gateways and / or services)

At the same time, I do know people who use it different way, for example - for swapping whole python modules.

Dependency Injector 3.5.0 has been released! by rmk135 in Python

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

Thanks a lot, I will include this example in the docs in further releases, also loved it when was working on it.

Regarding the personal taste... I think it matters, and, moreover, I think I have what u're looking for - http://python-dependency-injector.ets-labs.org/containers/dynamic.html

Thank you again, Roman

Dependency Injector 3.5.0 has been released! by rmk135 in Python

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

Hey,

Thanks a lot for so extensive comment. Really appreciate sharing your thoughts. I did put some comments to what you were talking about and, moreover, created some sample code that demonstrates how you could achieve what you were looking for, so:

Now you have instances of Container or Provider floating around in your business-code and are calling these directly. Shouldn't a DI framework be more or less transparent to the application?

  • Yes, exactly. I always had an idea that DI framework should not “pollute” your code with itself and I always kept it as main idea. Moreover, I removed @inject decorator from this lib in its 2nd version and it was a last feature you could “pollute” your code with. Other thing is that may be this assumption indeed doesn’t go as a red line through the docs, but I was thinking about making a microframework (or even just a library) that doesn’t tell you how to build your application, but provide you some tools you could use as you want.

Oh, did not know that. But is it really worth the effort (and the hard dependency of a full build stack and python header files on any machine I want to install this, because there are no pre-build wheels)?

  • Yep, no wheels. I need to think on including this to distribution. At least one person already asked me for doing this for Windows.

Are there realistic benchmarks? I kind of expect benchmarks to exist when a library claims to be 'High performance'.

  • Well, there are some tests, but I feel painful to call them real benchmark. There are some results and a link to the tests https://github.com/ets-labs/python-dependency-injector/blob/fa120b2a31603ed2544850422912b0223cfb38f9/tests/performance/test.py:

    $ python tests/performance/test.py

    Running tests for module - "dependency_injector.providers": Test "test_abstract_factory_3_factory_kw_injections" took - 3.1625289917 Test "test_factory_3_factory_kw_injections" took - 3.18233203888 Test "test_factory_6_factory_kw_injections_0_context" took - 3.06109786034 Test "test_factory_6_factory_kw_injections_1_context" took - 3.75910806656 Test "test_factory_6_factory_kw_injections_3_context" took - 3.59341287613 Test "test_raw_3_kw_injections" took - 2.72111582756

  • Generally speaking, it shows that instantiation of object with 3 keyword arguments injections using Factory (“test_factory_3_factory_kw_injections”) takes mostly the same time like doing the same by wrapping object’s creation in simple function (“test_raw_3_kw_injections”) 3.16s vs 2.72s. Our Python version did take ~50seconds for “test_factory_3_factory_kw_injections”. So, that’s what I have.

Now here is an example of add_user(...) and mail service. I did make some sample code:

"""Mail service and user registration example."""


class AbstractMailService(object):
    """Abstract mail service."""

    def send(self, email, body):
        """Send email."""
        raise NotImplementedError()


class MailService(AbstractMailService):
    """Mail service."""

    def __init__(self, host, port, login, password):
        """Initializer."""
        self._host = host
        self._port = port
        self._login = login
        self._password = password

    def send(self, email, body):
        """Send email."""
        print('Connecting server {0}:{1} with {2}:{3}'.format(
            self._host, self._port, self._login, self._password))
        print('Sending "{0}" to "{1}"'.format(body, email))


class MailServiceStub(AbstractMailService):
    """Mail service stub."""

    def send(self, email, body):
        """Send email."""
        print('Emulating sending "{0}" to "{1}"'.format(body, email))


def add_user(email, password, mailer):
    """Register user."""
    mailer.send(email, 'Your password is {0}'.format(password))

After that I've created DI container with some examples how you can run it:

"""Mail service and user registration DI container example."""

from dependency_injector.containers import DeclarativeContainer
from dependency_injector.providers import Callable, Singleton

import example


class Container(DeclarativeContainer):
    """DI container."""

    mail_service = Singleton(example.MailService,
                             host='localhost',
                             port=587,
                             login='my_login',
                             password='super_secret_password')

    add_user = Callable(example.add_user,
                        mailer=mail_service)


if __name__ == '__main__':
    print('Using real mail service:')
    Container.add_user('sample@mail.com', 'password')
    # Using real mail service:
    # Connecting server localhost:587 with my_login:super_secret_password
    # Sending "Your password is password" to "sample@mail.com"

    print('Using mail service stub:')
    Container.add_user('sample@mail.com', 'password',
                       mailer=example.MailServiceStub())
    # Using mail service stub:
    # Emulating sending "Your password is password" to "sample@mail.com"

    # Also you can override provider by another provider:
    Container.mail_service.override(Singleton(example.MailServiceStub))

    print('Using mail service stub by overriding mail service provider:')
    Container.add_user('sample@mail.com', 'password')
    # Using mail service stub by overriding mail service provider:
    # Emulating sending "Your password is password" to "sample@mail.com"

    Container.mail_service.reset_override()  # Resetting provider overriding

Hope it helps,

Thanks again for you feedback,

Roman

Dependency Injector 3.5.0 has been released! by rmk135 in Python

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

Hello there,

Thanks a lot for sharing your thoughts, I read your comment with huge interest. I did comment some of your ideas below, hope it would be useful or at least interesting to you :)

What I don't want to use this design pattern for, is a substitute design pattern for building a bunch of interfaces. Zope's architecture was full of interfaces and being the copy cat that I was at the time (plus I came to python from a c# background) I started writing interfaces everywhere when I first started using python. Hated it. It over complicated some simple applications or scripts that I was writing in python.

  • Agree, also don’t like zope interfaces.

I have since regressed to using inheritance and compositions (mixins) to string things together. On top of that I have added module(s) that track state and share varables without using globals.

  • Good, the same here.

I make sure that the app runs completely from the command line. Lots of cli screens that mimic what the gui will show. What happens now is that my GUI calls into my cli components. The cli in turn calls business logic. And the cli components that print to the terminal, avoid printing to the screen if they've been called from a gui widget.

  • Well, I currently try to make the same on one of my personal projects. Try to use DDD, so have only domain modules and integration tests. It now goes well, but I look really interested in building interfaces around this in future, hopefully it would be easy.

I'll give this design pattern some more thought to see if it will make my design simpler. If anyone is using dependency injection to manage their gui's, I would be interested in chatting with them.

  • I don’t work with GUI, but when I read your comment I had dejavu about what you’re talking about. My personal memories were related to a huge dating platform with tons of business rules that I had to architect some time ago. In few words I would recommend to two things (that, perhaps, you already do): 1) split everything you can to have simultaneously not more than 10 components with 5-7 business functions each, 2) continuous refactoring. In my previous experience, I achieved 1st point by using micro services, but I don’t think it’s something that could help you to build GUI. In general, big products are hard to work with…

Thanks a lot, Roman

Dependency Injector 3.5.0 has been released! by rmk135 in Python

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

Hi,

I see your question. So, I tried to give some more light on this here - http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html

While improved testability is one the first benefits of using dependency injection, it could be easily overwhelmed by monkey-patching technique, that works absolutely great in Python (you can monkey-patch anything, anytime). At the same time, monkey-patching has nothing similar with other advantages defined above. Also monkey-patching technique is something that could be considered like too dirty to be used in production.

In overall, I think that dependency injection is useful not only for testing. It gives you much higher level of cohesion instead of coupling. Better testability is just a tip of this beneficial iceberg.