Dependency injection framework is constructing the object first and then injecting the dependency which is incorrect

Bug #1180136 reported by Nachiappan
10
This bug affects 1 person
Affects Status Importance Assigned to Milestone
OpenStack Identity (keystone)
Won't Fix
Wishlist
Unassigned

Bug Description

Dependency injection framework is constructing the object first and then injecting the dependency which is incorrect.

def requires(*dependencies):
    """Inject specified dependencies from the registry into the instance."""
    def wrapper(self, *args, **kwargs):
        """Inject each dependency from the registry."""
        self.__wrapped_init__(*args, **kwargs) ----------------------> Constructs object First

        for dependency in self._dependencies: ----------------------> Inject dependency Second
            if dependency not in REGISTRY:
                raise UnresolvableDependencyException(dependency)
            setattr(self, dependency, REGISTRY[dependency])

It would be better to inject the dependency first and then construct the object, so that the dependency is available during object construction.

Revision history for this message
Adam Young (ayoung) wrote :

Is there a DI framework for Python that does this cleanly?

Changed in keystone:
importance: Undecided → Critical
Adam Young (ayoung)
Changed in keystone:
importance: Critical → Low
Nachiappan (nachiappan)
Changed in keystone:
assignee: nobody → Nachiappan (nachiappan-veerappan-nachiappan)
Dolph Mathews (dolph)
Changed in keystone:
status: New → Confirmed
Revision history for this message
David Stanek (dstanek) wrote :

I wrote a DI framework for Python called snake-guice that could replace this implementation. I would be pretty the existing requires and provides functions to use the snake-guice implementation. This gives you a doorway to the more advanced features.

Revision history for this message
Dolph Mathews (dolph) wrote :

I'd be happy to see the DI implementation replaced with something better - It was mostly implemented to achieve a refactor.

Revision history for this message
Dolph Mathews (dolph) wrote :

Unassigning due to inactivity.

Changed in keystone:
assignee: Nachiappan (nachiappan) → nobody
Changed in keystone:
assignee: nobody → Alexey Miroshkin (amirosh)
Revision history for this message
Alexey Miroshkin (amirosh) wrote :

The current DI implementation is very loosely coupled. Provider is registered when it is constructed somewhere, without providing any prior information. Technically we can't guarantee that a dependency will be injected before a consumer object construction, because a provider object could be instantiated later on. Moreover this problem is not applicable just to __init__ method, a consumer object is inconsistent state from a developer point of view and any methods which rely on injected attribute are vulnerable.

I see the following options:

1. I quickly implemented the injection before init by delaying init execution until all dependencies are satisfied. It works except the case of circular dependencies, but it breaks Python object construction protocol and leave an object in inconsistent state for some time. So in general there is no difference with the current situation or even make it worse. Moreover the case of circular dependencies is very problematic.

2. Add a callback method to the consumer, like _dependencies_injected and invoke it, if a particular consumer object has it, when all required dependencies are injected. Not so elegant as satisfy all dependencies before init, but gives a developer a right amount of control over an object state.

3. Make a framework more rigid, by registering providers as part of configuration step - for example like Guice module. It may require more changes but would give us a clear understanding of the object graph.

I'm sending a patch wich implements the second approach but let's discuss other options.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Fix proposed to keystone (master)

Fix proposed to branch: master
Review: https://review.openstack.org/117523

Changed in keystone:
status: Confirmed → In Progress
Igor (khapov-igor)
information type: Public → Public Security
information type: Public Security → Public
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on keystone (master)

Change abandoned by Alexey Miroshkin (<email address hidden>) on branch: master
Review: https://review.openstack.org/117523
Reason: Doesn't make sense anymore

Changed in keystone:
assignee: Alexey Miroshkin (amirosh) → nobody
David Stanek (dstanek)
Changed in keystone:
assignee: nobody → David Stanek (dstanek)
Revision history for this message
Morgan Fainberg (mdrnstm) wrote :

Moved to "wishlist" as this is a nice to have but it's not critical path at this point.

Changed in keystone:
importance: Low → Wishlist
Revision history for this message
Steve Martinelli (stevemar) wrote :

Automatically unassigning due to inactivity.

Changed in keystone:
assignee: David Stanek (dstanek) → nobody
status: In Progress → Triaged
David Stanek (dstanek)
Changed in keystone:
assignee: nobody → David Stanek (dstanek)
Revision history for this message
David Stanek (dstanek) wrote :

I still have a handful of patches that I am working on for this.

Revision history for this message
Lance Bragstad (lbragstad) wrote :

Automatically unassigning due to inactivity.

Changed in keystone:
assignee: David Stanek (dstanek) → nobody
Revision history for this message
Morgan Fainberg (mdrnstm) wrote :

Dependency Injection was removed/reworked. This is no longer an issue.

Changed in keystone:
status: Triaged → Won't Fix
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.