-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue with injecting Generic classes - broke in python 3.7 #175
Comments
As an interesting point of note, if I change the binding to bind to the exact generic type specified in the injectable class GenericClass[str], the same exception is thrown, but on instantiation of the injector instead: Changed code from unit test:
Test failure and stacktrace:
|
Hey, I don't think this was ever explicitly supported – if it works on some Python versions it's purely by accident. :) I'm not opposed to supporting this in principle, could you provide an example what would you use it for though? (A real example can affect the design decisions to make here) |
Hehe, that makes some sense, yeah, gotta love a happy accident. Still not quite got to the bottom of why it works in 3.6, but need to spend some time figuring out the differences in the typing module between 3.6 and 3.8 as well as remind myself of how the Injector internals work :D As to our use cases: One is a simple generic cache class, with generic types for the key and value types of the cached data. (essentially a more complicated dict) - the generic types used here in practice are a mixture of custom objects (URL objects as keys, with JenkinsAPI data objects as the values. Other implementations have str keys instead, but the value types could be pretty much anything. The other hasn't been written yet, but is likely to at some point soon, which is generic scanner, with generic types for the thing being scanned, and the result class to be returned. TypeVar for the generic result type here will likely be using bounds if that makes any difference? Is there any more detail you would need to influence design decisions? I could probably remove the generics usage if entirely necessary, or wrap over the generics with a concrete implementation, but would like to explore the options first that don't involve a load of boilerplate :) |
I'd ideally like to see some real world-like pieces of example code that demonstrates the need for this feature, from the description and from the unit tests given so far I'm not entirely convinced this is something that I want to support (but I still don't understand it fully, I admit). |
We're using parameterized generics to inject dependencies that use generic parameter to configure itself. For eg. here
|
Thank you – what's the |
Hi, I'm late to the party but can give an example on why this could be useful. Imagine you have ModelType = TypeVar("ModelType")
class ModelProvider(Generic[ModelType]):
def get_data()->list[ModelType]
"""returns a list of models from somewhere""" There's one implementation of the generic model provider per data source. So there's a The signature of the class that processes def MyModelProcessor:
"""reads data from the model provider and does something with them"""
@inject
def __init__(my_model_provider: ModelProvider[MyModel]):
self._my_model_provider = my_model_provider
def do_something():
models = self._my_model_provider.get_data()
# do something Now it'd be super nice to have something like this: def configure_for_database(binder):
"""used in integration tests and the running application"""
binder.bind(ModelProvider[MyModel], to=DatabaseMyModelProvider(host, usr, pwd))
def configure_for_database(binder):
"""used in simpler unittests"""
binder.bind(ModelProvider[MyModel], to=FileMyModelProvider(path_to_a_file_with_testdata)) Being able to easily switch between those different implementations is great and it de-couples the processing logic from the choice of the datasource. But currently, we cannot use the straight forward code above. But instead, we need a little bit of boilerplate, because we cannot bind class MyModelProvider(ModelProvider[MyModel], ABC):
"""this class is pure boilerplate"""
# here's nothing
pass
...
def configure_for_database(binder):
"""used in integration tests and the running application"""
binder.bind(MyModelProvider, to=to=DatabaseMyModelProvider(host, usr, pwd))
def configure_for_database(binder):
"""used in simpler unittests"""
binder.bind(MyModelProvider, to=FileMyModelProvider(path_to_a_file_with_testdata))
...
def MyModelProcessor:
"""reads data from the model provider and does something with them"""
@inject
def __init__(my_model_provider: MyModelProvider):
self._my_model_provider = my_model_provider This artificial class |
Thank you @hf-kklein, I think this is a convincing example. I'd be open to accept a PR implementing some support for that (there was #188 once upon a time). |
why isnt the pr mentioned in #188 was merged? if the issue is reopened? |
Hi there, currently upgrading a project using injector to python 3.8. We use MyPy generics in our project and I discovered that I can no longer inject my generic class.
On further investigation, this seems to be still present in master, works in python 3.6.8, then is broken in anything 3.7 and above.
The following unit test reproduces the issue:
Output on the failing python versions is:
Could do with getting this one fixed soon-ish, and will be taking a look tomorrow to see if I can create a PR that fixes it. If you have any hints or tips that might help, it would be appreciated :)
The text was updated successfully, but these errors were encountered: