Hey @ArjanCodes ! can you review `fast_depends` in relation to this video? the creator of the library attempted to extract the FastAPI DI Framework into a separate Library for people to use without FastAPI. I wonder how you would use it in the examples of this video..
Personally I wouldn't use the "inject" library for two reasons: when calling the functions, I don't know if it has the dependency injected, or doesn't at a glance, or if it has the right dependency injected; i also don't like relying on 3rd party libraries for this design-pattern-type of work - tomorrow they decide to change the way they initialize their injections, now I have to rebuild my project around that (unless I never update it). I do use fastAPI's dependency injection, because it explicitly states what's being injected and where.
To some point i get it, but lets be real here. EVERY third party library has that problem. Second thing is that FastAPI is just overhyped and their DI just sucks.
@@Nalewkarz *gasp* blasphemy! lol. you have a point, there's no perfect library or framework. I just found that fastapi does 90% of things that I need very well, and I cobble together the other 10%, what sold me on them is the documentation and executable examples.
I currently use spring boot in my work, and I hate the dependency injection of it. Coming from C/C++ background, I want to be able to see at a glance what object is being passed or created. Too many times some junior developer comes and asks me to solve their "bean not found" problem, if dependency injection wasn't used, they could solve it themselves because the error will be explicit.
That's like advocating for writing everything in assembly language just because you want to know exactly where every variable is in memory. Dependency injection allows you to structure the code in a modular way, making it more maintainable and scalable, and it reduces coupling. While configuration issues can be frustrating to troubleshoot, the flexibility and order it brings to the project make it well worth the effort. In the past, I hated it too. Then I matured
@@uvillanueva95 great hyperbolic argument. Because of course there is no middle ground in-between dependency injection and assembly, very mature of you to realise the black and white nature of the problem.
Correct me if I am wrong, but doesn't it violate the rule explicit better than implicit? You decorate your function with @inject and then may call the function without any arg, which will do something for you.
Nice video Arjan! First time I heard about this library and definitely I am going to use it in my projects. It would be great if you can record another video explaining how to override the dependencies for the tests.
For logging, you may want to look at loguru. It's as easy to use as doing `from loguru import logger` anywhere you need it. Assuming an application has a single entry point from which initialization code is run, it's only a matter of running some setup code for loguru once and the configuration will be applied everywhere the logger is imported. Pretty neat, and entirely solves the dependency injection issue!
Well thank you for reading our minds over distance 😆. We had a discussion on *exactly* this topic two days ago at the office and FastAPI came up as it does DI so cleanly. We were wondering whether there was a DI library for Python and "inject" definitely does fit that bill. *However*, I don't particularly like the way it handles the configuration/setup of the dependencies. FastAPI is much more intuitive. I'm really tempted to dig into the FastAPI implementation. Especially to see how it handles concurrency considering that "Depends(...)" is called during import-time. I have a good guess what's happening but I definitely want to dig in. It would be nice to have FastAPI style DI in non-web projects. We do DI a lot in our team but do it all manually. And it's really tough to strike a good balance between something that's not overly verbose but also explicit enough to stay maintainable. Thank you for this video and the shoutout to "inject". I will have a look and give it a trial run to see how it "feels". Even though my initial instinct is that it looks "too magical" I might get positively surprised.
It best to stay away from DI frameworks that require you to change your code with Python's decorators, descriptors etc, there are exceptions to this rule, but if almost every class needs to be adjusted in such way then it introduces sooo much coupling to a single library, and DI is actually a pattern that should lower coupling ^^ Not to mention that such design uses global state underneath.
@@Antash_That gels with my personal feelings. I love my code to be explicit. And so far all DI frameworks I've seen introduce way too much magic. And - as you rightly say - tightly couple the code with that framework. Take Spring from the Java world for example. It's hard to get Spring out of your system once it's in there.
2 that I know and used (a lot) are ets-labs dependency injector & proofit404 dependencies@@MichelAlbert I actually use them both at the same time, but on different levels of the architecture (it's a huuuge project). Both have their cons too though.
Absolutely! For programs with limbs in different areas, like scrapers or manipulating user data in productivity / organising apps. I think it'd be very handy to explicitly prescribe the control flow from the server for eg.
Very interesting! It looked a bit cumbersome at first, but when you start to think about it it makes a lot of sense. I have this project at work where we have one Backend system with a extensive API. I'm looking into making Python bindings to this API and defining one function to perform the query or provide a client (for example httpx) would make it so much easier to test and make the code way cleaner. Never heard about "inject" before, thanks Arjan! :)
I'm with you about that is not necessary to use an external library for dependency injection. The problem, for me, is that if you are injecting via constructor the dependencies, you'll need a lot of code to create the dependencies in the highest layers of your code. For example: if your controller use different services or use cases and each one of them needs repositories, your controller will need to create that repos and then create services and use cases injecting the repositories. And you have a lot of boilerplate code staining your highest level. If you implement something like a dependency container, this piece is the responsible of instantiate the different dependencies and you can use it whenever you want. I'm used to work with spring or micronaut in java and I'm surprised how immature other languages like python are (using Flask, probably using django or fastapi is better).
I am not sure if this inject framework really does help with maintainance and readability. I definitly directly want to see what parameters is needed for a given method. Using the framework I would dig deeper to see what the method acutall needs. Is this REALLY better? I would vote NO.
Imagine you are calling a dozen functions, some of which will access your database, some of which need to access a UserRepository, some of which need to access a PostRepository, some need the MailService, and some need the SNSService. How are you going to make sure every function has the classes it needs to perform its work without passing around giant globs of service instances and creating new instances all over the place?
Would love a video about your ideas on how to write good, testable Python code for AWS Lambda functions!! Also, hear your thoughts about AWS Lambda Powertools! BTW, your content is great, and I’ve learned so much from it! Keep being awesome!! 🤘🏻
Hi Arjan! I'm curious about your mention of logging as a DI use-case. My understanding is that it should never be necessary to pass around `logger` objects because they are singletons and can always be returned with `getLogger()`, even if they've been defined in some outer scope (though I guess this assumes that you **always** want to use one already-known, particular logger inside those functions).
Hi Andrew, I have some experience in passing around logger objects in my FastAPI applications' internal functions! One use case is that you want to maintain information of each API calls made to the application in your logger prefixes or header information and having a singleton would mean that instead of passing down the logger object, you pass down the API information to be instantiated with the logger - which is similar amount of work: (e.g. `logger.info(..., extra=api_info)` instead of `logger = CustomLogger(api_info) ... logger.info(...)`). I would think this would be a valid use case right?
As a beginner, I have a question. In the example at the beginning of the video, who not have do something as a class instance method. Isn't that effectively the same?
Dependency Injection can also be easily achieved using partial functions. As Arjan said, you create a "middleware" of sorts where in which you create a partial that provides the dependencies to your function. Let's say you've got a use case (in the form of a function, I see no need to create it as a class in Python), and this usecase depends on some databases interfaces. In the middleware file you provide the implementation to said interface to the usecase partial, and you call the partial building function from your endpoint. In this way you also comply with a clean code architecture, as you depend on abstractions and you don't worry about implementations in your business logic.
It is said, that it is easy to write decoupled code (or pure functions). Hard part is to glue it together. I use DI manly for testing so db_arg = None, and inside db_arg = db_arg or get_db() is fine.
dependency injection shines when you have a graph of dependencies. Your example is too simple and it does not require a dependency injection framework indeed. But when you want to configure graph objects that depends on particular types, dependency injection frameworks help. The FastAPI does not help much with graphs. It says it does, but it's coupled with FastAPI. If you are creating a UserRepository class, you don't want to add "Depends" function from FastApi on it. I'm using the lib "dependency_injector" which you can configure graphs of dependencies and it works really well.
I use dependency injection for sensor/actor access. If the device is present, it is used, if not, a mock device is used instead, allowing the program to run independent of hardware presence.
I may just never have had a complex enough use case, but the frameworks just feel like using fancy global. I prefer the explicit nature of passing the args
Agreed - I want to see the data flow. Inject seems to make functions that take no arguments, and you instead have to hunt down where the objects are created instead of it just being right in front of you
Inject looks a lot like a global functools partial to me. My big hang up with fastapi is the heavy use of globals in the documentation. You really have to dig and experiment to figure out how to have an API that is composed across many different decoupled files (e.g. using Routers.)
I think you should use your name more often, in the intro perhaps 'Hi, I'm Arjan'... Today I realised you used your name for what may have been the first time (for me). Before this I thought your name was pronounced 'R-Jarhn'... It sounded a lot nicer when you said it for real. BTW. Love the effort you put into your videos. Appreciate the detail and demonstrations.
I just wanted to ask: How Dependency Injection is similar/different to the classical Strategy design pattern? Personally, I don't see (right now) huge differences between the two ideas, but maybe _you_ can tell me?
What @Antash696 writes. For example you could inject a function into another function. This is still dependency injection, but different from the Strategy pattern.
@@ArjanCodes , well, the idea is almost the same: you've to pass a Strategy object to a constructor to use different strategies through a parent/abstract class interface methods... Can you pass more examples for the DI usage in Python, pls?
Dependency injection framework looks cool, seems like ur partializing like a set of functions with related objects - also seems like this could be a class. Im a bit of a fan of the more functional style - so this seems cool, imho
I missed a word of caution to DI together with demos. DI is a heavy weight 3rd party dependency. How does the error message looks if you have two or more injection candidates? Then you have to give names to your injected instance or something like this. Like if you need a second sqlite3.Connection in the example. What if everybody uses it and already a sqlite3.Connection comes with a 3rd party library. Then you have to search these builder methods in some 3rd party dependency or do you do then some monkey patching? Only showing the happy path looks smooth but do you poison your code with this?
For logging I use Python builtins and just expose a logger object that's made singleton through an inheritance of a Singleton class that I made which has a __new__ method that does an increment check. I don't need to import anywhere and it's usable everywhere.
[7:25] how do we test the `bind()` method if we have to? i use Python as my main language (projects and work) but i am in love with the DI from PHP / Java, i am not totallty sure how this gonna work with Python in the long run.
10:46 Why is there only `yield db` inside the try block? I guess this cannot raise an exception in any circumstances. Shouldn't the `db = SessionLocal()` also be inside the try block?
It's correct. The try-except is run on the outside of the calling function, so that if your function that uses get_db() throws an error then the db connection still gets closed. The flow goes: FastAPI calls your get_db dependency, then yield db to your route function. Then if your route function throws an error, the finally: clause is called and the connection is closed. And if your function doesn't throw an error, the finally clause is still called and it closes the connection.
I prefer dependency injection. For everything outside FastAPI I've just written my own decorators, used Pytest, or used the factory as a default parameter and call it in the function. The decorator needs to have a way to override the injection, I didn't see if Inspect has that ability.
When there are deeply nested dependencies, DI frameworks can be a saver. However, I don't like the way inject does it with decorators. I prefer less intrusive ways like punq, which would be easier to replace and gives me enough control. I feel that the dependency injector framework has too much boilerplate and makes the code confuse. I also tried the dependencies framework, however, it caused some problems, since you get an instance by requesting a class, and it seems that it is no longer maintained.
`dependencies` has less boilerplate than `dependency injector` but in a large project when you need a lot of "scopeing" the boilerplate will blast in size. In general I dont see a good, maintained DI framework that ticks all my boxes in Python right now ;(
Hey Arjan very nice video as always, thank you very much! I have a question about Depends in FastAPI. Is it so that the DI works only with route handler or would it be possible to add some abstraction layer, let's say a repository and have the db injected there? Or is it only going to possible by passing it from the route handler into the repo via constructor?
I also tend to reach for constructor dependency injection (by pattern, not by library or framework), which works great until the root main function has dozens of parameters and all the classes have some subset of those parameters. Then it stops being easy. My main gripe with dep injection frameworks is the docs often try to hide how they work, making it seem like too much black magic and I don't need black magic in my code. (e.g. where is it constructed, when, how? someone is making those decisions, just in a different code file)
A bigger FastAPI app usually has a bigger main function for manually constructing the app by calling include_router, add_midddleware, etc. On the other hand, the main function of a Java Web application using SpringBoot is usually a single line calling "run()" with an application class regardless of the application size. Why? Because SpringBoot applications use Spring DI framework to construct the applications themselves (a router is a dependency of an app, for example). As this video explains, generic DI frameworks can manage the lifecycle of objects. Generic DI frameworks in Java, like Spring, can also apply IoC to the entire application lifecycle. Which one is better? I use both Python and Java in my work, but I prefer SpringBoot's approach, particularly for big applications. One of the points frequently missed about DI in Java is "convention over configuration (CoC)". DI frameworks in Java encourage programmers to use CoC to minimize boilerplate configuring DI. This has a good effect on maintaining code consistency in a big project.
I wonder if `inject` can help deal with mocking dependencies where in production you use an async function but in testing everything is synchronous. Lately I've had to deal with type checking in our code and had to explicitly ignore some function signatures because the type definitions created conflicts. So off-loading these dependencies to a library like inject maybe can make this a bit cleaner and not have the static analysis trip over such details.
I'm not much of a python developer but this pattern is used frequently in Java. In java you can inject an ExecutorService which is an abstraction around a thread pool and in production code use an actual thread pool then in tests use a "direct" executor that executes logic inline. I'm not sure if a similar pattern would apply in Python. For languages that use async/ await (or similar) keywords I could see how this would be more difficult to implement
There is a book called "A philosophy of software design" by John Ousterhout that really made me think about things like this. He is talking about deep and shallow modules. I think that a dependency injection framework would fall in the category of a shallow module and should be avoided.
That was my spidey-sense as well. Agree with the other comments that I prefer the injection to be explicit where it happens so it's apparent when one is reading just that chunk of code. But... would have to test drive this shiny 'new' (to me) toy to see if there are use cases for it that work for me. (dang-it, Arjan, you keep turning us on to new toys and I can't keep up with trying them out!)
I would argue this: if you learn a new programming language, make your own dependency injection library for it. Why? It will teach you all the little tricks and quirks of the language. From the basics of primitives, to the reflection capabilities and all in between. After you're done and you know your language, look around what libraries are out there. Found one that you like? Use it. Otherwise use your own and refine it along the way.
So let me get this straight, any time you pass an argument to a method you are doing dependency injection? So basically every line of code ever written that passes parameters is doing DI? That doesn't make any sense. By extrapolation, that would then mean any code that that calls a parameterized method is actually an inversion of control container? HashMap map = new HashMap(10); So that's dependency injection, and this comment is now an open source DI and IoC container? Dependency Injection - I don't think that word means what you think it means. 🤦♂
DI just makes code more complex and unobvious. Approaches like this make frameworks a set of magic. "Explicit is better than implicit" they said. Lately I've seen an increasing tendency to switch to Go in part because of these stuff in Python.
For pyrhon ive found, load_dotenv and theres this override package as well, and a pattern thats very similar to dataclass but is "baseclass" super interesting. Im also starting to see non-standard usage of init files... in certain designs if the folder are all implementations then __init__ is the base class. Working on deploying some open source, extending it, as well as contributing back bug fixes. I also had this weird thought that modules (a file is just a class), and like closures are just calling modules with variables defined at call (the state) - which is equivalent to their oop defn for class instances(?). And ive always implemented singletons as just only class methods... not sure if there is a betrerway (and have no init), those just feel like a module that has all variable refrences in the module itself! 😅 But meh! Just a thought how closures, classes, modules etc are just all an implementation of abstraction 🙂
I remember having some trouble recently, because they changed when the finally block is executed. Basically I yielded a path of a folder and unlinked it in the finally block. The change was, that the finally block was executed before the response. This resulted in the file i wanted to return was getting deleted before the FileResponse could return :D Took me some time to find that^^
DI Frameworks in Python are an unreadable mess of buggy 'magic', that just encourage people to write code that is not only broken without the framework, but butt ugly. Want to call a function without specifying dependencies? Just give its args default values FFS (if you want them lazy loaded, default values of None, then arg = arg or ExpensiveResource()).
Hey Arjan! Thank you for your content. You can integrate FastAPI with PyNest, and inject PyNest's service layer into the FastAPI controllers. It would be great if you can do a video about PyNest and FastAPI integration.
I would not use inject because: + I don't see the point to depend on a third party library for this kind of work, + I like to use dependency injection in a simple yet comprehensive way
Be careful guys, DI frameworks will make your code unreadable and even harder to debug for a person new to your codebase. Just self made IoC can be considered.
👷 Join the FREE Code Diagnosis Workshop to help you review code more effectively using my 3-Factor Diagnosis Framework: www.arjancodes.com/diagnosis
Is the diagnosis link working right?
I followed the link, filled the box, and ... waited for a week and got no mail. 😢
Hey @ArjanCodes ! can you review `fast_depends` in relation to this video? the creator of the library attempted to extract the FastAPI DI Framework into a separate Library for people to use without FastAPI. I wonder how you would use it in the examples of this video..
As an author of dependency injection library who maintain it for last 10 years I would be happy if people just care about IoC. That's would be enough.
I should know this, but I had to look up IoC to remember it was Inversion of Control
Personally I wouldn't use the "inject" library for two reasons: when calling the functions, I don't know if it has the dependency injected, or doesn't at a glance, or if it has the right dependency injected; i also don't like relying on 3rd party libraries for this design-pattern-type of work - tomorrow they decide to change the way they initialize their injections, now I have to rebuild my project around that (unless I never update it).
I do use fastAPI's dependency injection, because it explicitly states what's being injected and where.
To some point i get it, but lets be real here. EVERY third party library has that problem. Second thing is that FastAPI is just overhyped and their DI just sucks.
@@Nalewkarz *gasp* blasphemy! lol.
you have a point, there's no perfect library or framework. I just found that fastapi does 90% of things that I need very well, and I cobble together the other 10%, what sold me on them is the documentation and executable examples.
Exactly, Explicit is better than implicit, check out Dependency Injector library
what would you do if you are using something without dependency injection tool like flask?
I currently use spring boot in my work, and I hate the dependency injection of it.
Coming from C/C++ background, I want to be able to see at a glance what object is being passed or created.
Too many times some junior developer comes and asks me to solve their "bean not found" problem, if dependency injection wasn't used, they could solve it themselves because the error will be explicit.
This to me is just looking like global variables with extra steps.
That's like advocating for writing everything in assembly language just because you want to know exactly where every variable is in memory. Dependency injection allows you to structure the code in a modular way, making it more maintainable and scalable, and it reduces coupling. While configuration issues can be frustrating to troubleshoot, the flexibility and order it brings to the project make it well worth the effort. In the past, I hated it too. Then I matured
@@uvillanueva95 great hyperbolic argument. Because of course there is no middle ground in-between dependency injection and assembly, very mature of you to realise the black and white nature of the problem.
It really depends. (I just injected a Dad joke)
Should have left it at the joke. Explaining it was anticlimactic. Nice one though
y made my day 😂
ah!
@@spacebuddy5339Then again, the explanation also had a joke.
I (dependen)see what you did there.
Correct me if I am wrong, but doesn't it violate the rule explicit better than implicit? You decorate your function with @inject and then may call the function without any arg, which will do something for you.
Nice video Arjan! First time I heard about this library and definitely I am going to use it in my projects.
It would be great if you can record another video explaining how to override the dependencies for the tests.
For logging, you may want to look at loguru. It's as easy to use as doing `from loguru import logger` anywhere you need it. Assuming an application has a single entry point from which initialization code is run, it's only a matter of running some setup code for loguru once and the configuration will be applied everywhere the logger is imported. Pretty neat, and entirely solves the dependency injection issue!
Well thank you for reading our minds over distance 😆. We had a discussion on *exactly* this topic two days ago at the office and FastAPI came up as it does DI so cleanly. We were wondering whether there was a DI library for Python and "inject" definitely does fit that bill. *However*, I don't particularly like the way it handles the configuration/setup of the dependencies. FastAPI is much more intuitive. I'm really tempted to dig into the FastAPI implementation. Especially to see how it handles concurrency considering that "Depends(...)" is called during import-time. I have a good guess what's happening but I definitely want to dig in. It would be nice to have FastAPI style DI in non-web projects.
We do DI a lot in our team but do it all manually. And it's really tough to strike a good balance between something that's not overly verbose but also explicit enough to stay maintainable.
Thank you for this video and the shoutout to "inject". I will have a look and give it a trial run to see how it "feels". Even though my initial instinct is that it looks "too magical" I might get positively surprised.
It best to stay away from DI frameworks that require you to change your code with Python's decorators, descriptors etc, there are exceptions to this rule, but if almost every class needs to be adjusted in such way then it introduces sooo much coupling to a single library, and DI is actually a pattern that should lower coupling ^^ Not to mention that such design uses global state underneath.
@@Antash_That gels with my personal feelings. I love my code to be explicit. And so far all DI frameworks I've seen introduce way too much magic. And - as you rightly say - tightly couple the code with that framework. Take Spring from the Java world for example. It's hard to get Spring out of your system once it's in there.
2 that I know and used (a lot) are ets-labs dependency injector & proofit404 dependencies@@MichelAlbert I actually use them both at the same time, but on different levels of the architecture (it's a huuuge project). Both have their cons too though.
See `fast_depends` on PyPI. It might solve your problem.
Absolutely! For programs with limbs in different areas, like scrapers or manipulating user data in productivity / organising apps. I think it'd be very handy to explicitly prescribe the control flow from the server for eg.
Very interesting! It looked a bit cumbersome at first, but when you start to think about it it makes a lot of sense. I have this project at work where we have one Backend system with a extensive API. I'm looking into making Python bindings to this API and defining one function to perform the query or provide a client (for example httpx) would make it so much easier to test and make the code way cleaner.
Never heard about "inject" before, thanks Arjan! :)
Nice job Arjan, great video ... Nest.js and Angular both have this DIP paradigm in its core, and it's great!! tks a lot
Thank you!
Haven't used such frameworks. I have a feeling they would make debugging harder 😆
I'm with you about that is not necessary to use an external library for dependency injection. The problem, for me, is that if you are injecting via constructor the dependencies, you'll need a lot of code to create the dependencies in the highest layers of your code. For example: if your controller use different services or use cases and each one of them needs repositories, your controller will need to create that repos and then create services and use cases injecting the repositories. And you have a lot of boilerplate code staining your highest level. If you implement something like a dependency container, this piece is the responsible of instantiate the different dependencies and you can use it whenever you want. I'm used to work with spring or micronaut in java and I'm surprised how immature other languages like python are (using Flask, probably using django or fastapi is better).
I am not sure if this inject framework really does help with maintainance and readability. I definitly directly want to see what parameters is needed for a given method. Using the framework I would dig deeper to see what the method acutall needs. Is this REALLY better? I would vote NO.
Imagine you are calling a dozen functions, some of which will access your database, some of which need to access a UserRepository, some of which need to access a PostRepository, some need the MailService, and some need the SNSService. How are you going to make sure every function has the classes it needs to perform its work without passing around giant globs of service instances and creating new instances all over the place?
Would love a video about your ideas on how to write good, testable Python code for AWS Lambda functions!!
Also, hear your thoughts about AWS Lambda Powertools!
BTW, your content is great, and I’ve learned so much from it! Keep being awesome!! 🤘🏻
Thanks for the suggestions Igor - and glad to hear the content is helpful 😎
Hi Arjan! I'm curious about your mention of logging as a DI use-case. My understanding is that it should never be necessary to pass around `logger` objects because they are singletons and can always be returned with `getLogger()`, even if they've been defined in some outer scope (though I guess this assumes that you **always** want to use one already-known, particular logger inside those functions).
Hi Andrew, I have some experience in passing around logger objects in my FastAPI applications' internal functions! One use case is that you want to maintain information of each API calls made to the application in your logger prefixes or header information and having a singleton would mean that instead of passing down the logger object, you pass down the API information to be instantiated with the logger - which is similar amount of work: (e.g. `logger.info(..., extra=api_info)` instead of `logger = CustomLogger(api_info) ... logger.info(...)`). I would think this would be a valid use case right?
As a beginner, I have a question. In the example at the beginning of the video, who not have do something as a class instance method. Isn't that effectively the same?
Dependency Injection can also be easily achieved using partial functions. As Arjan said, you create a "middleware" of sorts where in which you create a partial that provides the dependencies to your function. Let's say you've got a use case (in the form of a function, I see no need to create it as a class in Python), and this usecase depends on some databases interfaces. In the middleware file you provide the implementation to said interface to the usecase partial, and you call the partial building function from your endpoint. In this way you also comply with a clean code architecture, as you depend on abstractions and you don't worry about implementations in your business logic.
It is said, that it is easy to write decoupled code (or pure functions). Hard part is to glue it together.
I use DI manly for testing so db_arg = None, and inside db_arg = db_arg or get_db() is fine.
dependency injection shines when you have a graph of dependencies. Your example is too simple and it does not require a dependency injection framework indeed. But when you want to configure graph objects that depends on particular types, dependency injection frameworks help. The FastAPI does not help much with graphs. It says it does, but it's coupled with FastAPI. If you are creating a UserRepository class, you don't want to add "Depends" function from FastApi on it. I'm using the lib "dependency_injector" which you can configure graphs of dependencies and it works really well.
I use dependency injection for sensor/actor access. If the device is present, it is used, if not, a mock device is used instead, allowing the program to run independent of hardware presence.
I may just never have had a complex enough use case, but the frameworks just feel like using fancy global. I prefer the explicit nature of passing the args
Agreed - I want to see the data flow. Inject seems to make functions that take no arguments, and you instead have to hunt down where the objects are created instead of it just being right in front of you
Inject looks a lot like a global functools partial to me.
My big hang up with fastapi is the heavy use of globals in the documentation. You really have to dig and experiment to figure out how to have an API that is composed across many different decoupled files (e.g. using Routers.)
I think you should use your name more often, in the intro perhaps 'Hi, I'm Arjan'...
Today I realised you used your name for what may have been the first time (for me). Before this I thought your name was pronounced 'R-Jarhn'... It sounded a lot nicer when you said it for real.
BTW. Love the effort you put into your videos. Appreciate the detail and demonstrations.
I just wanted to ask: How Dependency Injection is similar/different to the classical Strategy design pattern? Personally, I don't see (right now) huge differences between the two ideas, but maybe _you_ can tell me?
DI is a broader concept, lots of OOP (and not only) patterns use DI
What @Antash696 writes. For example you could inject a function into another function. This is still dependency injection, but different from the Strategy pattern.
@@ArjanCodes , well, the idea is almost the same: you've to pass a Strategy object to a constructor to use different strategies through a parent/abstract class interface methods... Can you pass more examples for the DI usage in Python, pls?
About logging, what are your concerns about creating logger objects at the top level of a module, and using that logger at the available scope?
do we have any test sample as mentioned in 11:51?
What's about "dishka"?
Dependency injection framework looks cool, seems like ur partializing like a set of functions with related objects - also seems like this could be a class.
Im a bit of a fan of the more functional style - so this seems cool, imho
Why is yield used in get_db? I see how it can help dealing with exceptions within the correct context, but what makes the finally block execute?
I missed a word of caution to DI together with demos. DI is a heavy weight 3rd party dependency. How does the error message looks if you have two or more injection candidates? Then you have to give names to your injected instance or something like this. Like if you need a second sqlite3.Connection in the example. What if everybody uses it and already a sqlite3.Connection comes with a 3rd party library. Then you have to search these builder methods in some 3rd party dependency or do you do then some monkey patching? Only showing the happy path looks smooth but do you poison your code with this?
Yea I really wish examples used Postgres connection pools instead of SQLite so it were closer to real world usage.
For logging I use Python builtins and just expose a logger object that's made singleton through an inheritance of a Singleton class that I made which has a __new__ method that does an increment check. I don't need to import anywhere and it's usable everywhere.
[7:25] how do we test the `bind()` method if we have to?
i use Python as my main language (projects and work) but i am in love with the DI from PHP / Java, i am not totallty sure how this gonna work with Python in the long run.
10:46 Why is there only `yield db` inside the try block? I guess this cannot raise an exception in any circumstances. Shouldn't the `db = SessionLocal()` also be inside the try block?
It's correct. The try-except is run on the outside of the calling function, so that if your function that uses get_db() throws an error then the db connection still gets closed. The flow goes: FastAPI calls your get_db dependency, then yield db to your route function. Then if your route function throws an error, the finally: clause is called and the connection is closed. And if your function doesn't throw an error, the finally clause is still called and it closes the connection.
I prefer dependency injection. For everything outside FastAPI I've just written my own decorators, used Pytest, or used the factory as a default parameter and call it in the function.
The decorator needs to have a way to override the injection, I didn't see if Inspect has that ability.
You should try kink dependency injection library. It is very simple and I would say that it is more pythonic
When there are deeply nested dependencies, DI frameworks can be a saver. However, I don't like the way inject does it with decorators. I prefer less intrusive ways like punq, which would be easier to replace and gives me enough control. I feel that the dependency injector framework has too much boilerplate and makes the code confuse. I also tried the dependencies framework, however, it caused some problems, since you get an instance by requesting a class, and it seems that it is no longer maintained.
`dependencies` has less boilerplate than `dependency injector` but in a large project when you need a lot of "scopeing" the boilerplate will blast in size. In general I dont see a good, maintained DI framework that ticks all my boxes in Python right now ;(
Dependency injection is very good mechanism for MVC or MVP pattern, where you can separate conponents.
that’s exactly the application where I use it the most.
Hey Arjan very nice video as always, thank you very much! I have a question about Depends in FastAPI. Is it so that the DI works only with route handler or would it be possible to add some abstraction layer, let's say a repository and have the db injected there? Or is it only going to possible by passing it from the route handler into the repo via constructor?
try using `fast_depends`. Note: I am NOT the creator of that library. I just used it once for a project with mixed results.
Anyone say whay font and theme was Arjan using.It looks cool..
I also tend to reach for constructor dependency injection (by pattern, not by library or framework), which works great until the root main function has dozens of parameters and all the classes have some subset of those parameters. Then it stops being easy. My main gripe with dep injection frameworks is the docs often try to hide how they work, making it seem like too much black magic and I don't need black magic in my code. (e.g. where is it constructed, when, how? someone is making those decisions, just in a different code file)
A bigger FastAPI app usually has a bigger main function for manually constructing the app by calling include_router, add_midddleware, etc.
On the other hand, the main function of a Java Web application using SpringBoot is usually a single line calling "run()" with an application class regardless of the application size.
Why? Because SpringBoot applications use Spring DI framework to construct the applications themselves (a router is a dependency of an app, for example). As this video explains, generic DI frameworks can manage the lifecycle of objects. Generic DI frameworks in Java, like Spring, can also apply IoC to the entire application lifecycle.
Which one is better? I use both Python and Java in my work, but I prefer SpringBoot's approach, particularly for big applications.
One of the points frequently missed about DI in Java is "convention over configuration (CoC)". DI frameworks in Java encourage programmers to use CoC to minimize boilerplate configuring DI. This has a good effect on maintaining code consistency in a big project.
Lagom is nice, Pythonic dependency injection library.
I can’t use DI with Luau because it disables optimizations 😢
I wonder if `inject` can help deal with mocking dependencies where in production you use an async function but in testing everything is synchronous. Lately I've had to deal with type checking in our code and had to explicitly ignore some function signatures because the type definitions created conflicts. So off-loading these dependencies to a library like inject maybe can make this a bit cleaner and not have the static analysis trip over such details.
I'm not much of a python developer but this pattern is used frequently in Java. In java you can inject an ExecutorService which is an abstraction around a thread pool and in production code use an actual thread pool then in tests use a "direct" executor that executes logic inline.
I'm not sure if a similar pattern would apply in Python. For languages that use async/ await (or similar) keywords I could see how this would be more difficult to implement
There is a book called "A philosophy of software design" by John Ousterhout that really made me think about things like this. He is talking about deep and shallow modules. I think that a dependency injection framework would fall in the category of a shallow module and should be avoided.
That was my spidey-sense as well. Agree with the other comments that I prefer the injection to be explicit where it happens so it's apparent when one is reading just that chunk of code. But... would have to test drive this shiny 'new' (to me) toy to see if there are use cases for it that work for me. (dang-it, Arjan, you keep turning us on to new toys and I can't keep up with trying them out!)
I would argue this: if you learn a new programming language, make your own dependency injection library for it.
Why? It will teach you all the little tricks and quirks of the language. From the basics of primitives, to the reflection capabilities and all in between.
After you're done and you know your language, look around what libraries are out there. Found one that you like? Use it. Otherwise use your own and refine it along the way.
so nicely explained thank u so much🤝
I'm glad you enjoyed it!
So let me get this straight, any time you pass an argument to a method you are doing dependency injection?
So basically every line of code ever written that passes parameters is doing DI? That doesn't make any sense.
By extrapolation, that would then mean any code that that calls a parameterized method is actually an inversion of control container?
HashMap map = new HashMap(10);
So that's dependency injection, and this comment is now an open source DI and IoC container?
Dependency Injection - I don't think that word means what you think it means.
🤦♂
DI just makes code more complex and unobvious. Approaches like this make frameworks a set of magic. "Explicit is better than implicit" they said. Lately I've seen an increasing tendency to switch to Go in part because of these stuff in Python.
For pyrhon ive found, load_dotenv and theres this override package as well, and a pattern thats very similar to dataclass but is "baseclass" super interesting. Im also starting to see non-standard usage of init files... in certain designs if the folder are all implementations then __init__ is the base class.
Working on deploying some open source, extending it, as well as contributing back bug fixes.
I also had this weird thought that modules (a file is just a class), and like closures are just calling modules with variables defined at call (the state) - which is equivalent to their oop defn for class instances(?). And ive always implemented singletons as just only class methods... not sure if there is a betrerway (and have no init), those just feel like a module that has all variable refrences in the module itself! 😅
But meh! Just a thought how closures, classes, modules etc are just all an implementation of abstraction 🙂
Try out pydantic-settings! It handles configurations elegantly.
Thanks Arjan
Glad it helped!
Thanks for the content
My pleasure!
Super useful!
Why haven't y'all been using the Spring Java Framework for the last 20 years?
I remember having some trouble recently, because they changed when the finally block is executed. Basically I yielded a path of a folder and unlinked it in the finally block. The change was, that the finally block was executed before the response. This resulted in the file i wanted to return was getting deleted before the FileResponse could return :D Took me some time to find that^^
DI Frameworks in Python are an unreadable mess of buggy 'magic', that just encourage people to write code that is not only broken without the framework, but butt ugly.
Want to call a function without specifying dependencies? Just give its args default values FFS (if you want them lazy loaded, default values of None, then arg = arg or ExpensiveResource()).
Hey Arjan! Thank you for your content.
You can integrate FastAPI with PyNest, and inject PyNest's service layer into the FastAPI controllers.
It would be great if you can do a video about PyNest and FastAPI integration.
I would not use inject because:
+ I don't see the point to depend on a third party library for this kind of work,
+ I like to use dependency injection in a simple yet comprehensive way
Implicit garbage that only complicates the code
Why don't you ever pronounce the "i" in the word "repository". You always say "repostory"?
I don't think I will use this in the near future to be honest. Seems too much of a boilerplate
Wow what really needs
Be careful guys, DI frameworks will make your code unreadable and even harder to debug for a person new to your codebase. Just self made IoC can be considered.
Such a killer joke Quite Laughable (SQL) 😂 be careful this injection won't kill your database.