Comments (8)
PRs are welcome, and it seems to me like a natural evolution of Injector to support this.
But regarding the API, don't you think it would be better to bind the Annotated[...]
type instead of adding the named
argument?
# ...
LoginUsername = Annotated[str, Name("login_username")]
LoginPassword = Annotated[str, Name("login_password")]
@dataclass
def Login:
username: LoginUsername
password: LoginPassword
def configure_from_env(binder):
binder.bind(LoginUsername, to=lambda: os.environ["LOGIN_USERNAME"])
binder.bind(LoginPassword, to=lambda: os.environ["LOGIN_PASSWORD"])
I hope the performance hit of NewType
is negligible for things that run on Python, but the usability improvement of Annotated
is appealing.
from injector.
I just opened a PR a that adds support for Annotated
. Sorry @jriddy , I kind of needed this and it seemed pretty straightforward (unless I missed something). Would definitely value your input!
from injector.
I just to add one more data point. Interestingly, if I have the binding configured like this:
def configure(self, binder):
binder.bind(Annotated[Shape, 'first'], to=Circle)
binder.bind(Annotated[Shape, 'second'], to=Square)
and I get the objects directly from the injector, it works:
injector = Injector(Mod())
injector.get(Annotated[Shape, 'first'])
injector.get(Annotated[Shape, 'second'])
This means that the following provider method that manually constructs the A
object also works:
@provider
def provide_a_from_injector(self, injector: Injector) -> A:
return A(
injector.get(Annotated[Shape, 'first']),
injector.get(Annotated[Shape, 'second'])
)
While this is a potential workaround, I feel like the overall behaviour is too inconsistent to warrant a production use of the framework.
from injector.
I haven't had the time to get into all the details of this issue, but have you tried using NewType
instead of Annotated
? There's an example in the tests.
from injector.
I have, but unfortunately it doesn't work in situations where I want to inject a ClassAssistedBuilder
. For example, let's say I define a type FirstShape = NewType('FirstShape', Shape)
. Then, in class A
, I inject a ClassAssistedBuilder[FirstShape]
. When I call build()
, I get:
injector.CallError: Call to NewType.__new__() failed: object.__new__(X): X is not a type object (NewType) (injection stack: [])
from injector.
Would the the regular AssistedBuilder
work for you here? I haven't tried it myself, but it uses the existing binds so it might work better.
from injector.
I've been looking at this an I'm thinking about generalizing this pattern. If PRs are welcome I might give it a go to try to cook something up here.
I'm trying to evaluate an ad-hoc injection framework I can use or modify, but the limitation of needing to inject a type is kinda frustrating. It basically pushes you in the direction of using DI solely for creating more complex types that are worth constructing, whereas I often want to encourage devs to do things like express getting an environment variable as a dependency.
FastAPI has a DI implementation that is a good example of this, where it's injecting query strings and stuff from having parsed an HTTP request, but often the underlying types are simple types like str
and int
. It uses Annotated
to declare this metadata and then extracts that to create the binding.
While the design there is a bit different, since FastAPI is an application framework instead of a general-purpose library, I think this pattern could be applied here, such as in this putative code:
from dataclasses import dataclass
from injector import inject, Injector, Name
from typing import Annotated
import os
@inject
@dataclass
def Login:
username: Annotated[str, Name("login_username")]
password: Annotated[str, Name("login_password")]
def configure_from_env(binder):
binder.bind(str, named="login_username", to=lambda: os.environ["LOGIN_USERNAME"])
binder.bind(str, named="login_password", to=lambda: os.environ["LOGIN_PASSWORD"])
injector = Injector([configure_from_env])
injector.get(Login)
If there's interest I could look into adding this
from injector.
I haven't had the time to get into all the details of this issue, but have you tried using
NewType
instead ofAnnotated
? There's an example in the tests.
NewType
works sorta, but it leads to an annoying situation where you have to convert everything to your NewType
where you use them. And unlike Haskell or Rust or languages like that where this idea is really prevalent, using NewTypes in Python don't get compiled away and do incur a (small) runtime penalty.
from injector.
Related Issues (20)
- Support pep 604 style for optional types (`Type | None`)
- Bind a provider to multiple interfaces?
- Binding multiple interfaces to same implementation HOT 2
- Singletons for parent/child injectors are order-dependent HOT 9
- Maintainer status? HOT 2
- Potential resource leak HOT 3
- Is it an antipattern to have multiple instances of Injector (because of missing Generics support)? HOT 2
- Testing example in docs is not working. ImportError: cannot import name 'with_injector' from 'injector' HOT 6
- FastAPI support HOT 2
- Injection of two abstract classes and annotation
- Question about the example code and intended use HOT 2
- Differentiating between multiple providers HOT 4
- Providing different instances of the same type in the same module HOT 1
- Issue with ClassAssistedBuilder HOT 4
- Implementation of SessionScope Needed
- [Docs] No declarations of type `Name` and `Description` on Docs injection sample HOT 1
- use specific Injector instance as `inject` decorator HOT 5
- How to handle resources? HOT 5
- `injector.Injector.create_object` on pydantic-settings `BaseSettings` does not without factory function in Injector HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from injector.