Object Dependency Lifecycle

bp object-dependency-lifecycle

The way dependency injection (DI) is currently implemented in Keystone is not really using the “dependency injection” pattern. This spec is focused on improving the internal-to-Keystone object lifecycle (including dependencies).

This is based off of a Kilo Summit session: Pre-session Reading

Problem Description

  • What should the dependency injection pattern in Keystone look like?

  • How do we deal with Optional dependencies?

  • What is the lifecycle of objects (controllers, managers, etc.)?

  • Remove the magic of just adding attributes on to an object without really having them defined in the API

  • Optional dependencies are currently not implemented in a very object oriented way; we use the existence of the dependency as a sort of feature flag

  • Better seams for testing; specifically optional dependencies

  • DI is about dynamically building object graphs where different implementations of some interface can be used. We don’t really do that.

  • Keep APIs as backward compatible as possible (drivers for sure, managers and controllers to a lesser extent)

  • Compatibility with Stevedore. We may be looking to use Stevedore for dynamically loading drivers and we should make sure we don’t do anything that would make that harder or impossible.

What is dependency injection?

Wikipedia has a good description of dependency injection. In short the pattern is a way of separating the code that implements the business logic from the code that creates objects. Instead of creating dependencies, objects will expect those dependencies to be passed to its constructor. This allows the construction of object graphs (the tree of objects) to vary without changes to the objects themselves.

Proposed Change

  1. Phase I - Manual DI

  • This keeps the object lifecycle of the injected objects as singleton

  • This also keeps a global registry

  • Keep singleton scope on the current object graphs

  • Implement optional dependencies using the __init__ as optional kwargs

  • This will keep the internal code of the objects the same. We would still ‘if self.mydep’

  • Pitfalls:

  • We need to add something to the wire up next things. Right now through the magic of the decorator this just happens.

  1. Phase II - Evaluate Manual DI

  • Is there a need for a framework?

  • Is there enough of a pain point to use a DI framework?

  • Is there enough need of some of a DI framework’s features to use it?

  • Keystone may not be complicated enough to need anything more.

  • There are a few existing frameworks like inject and snake-guice

  • Do we need objects that are scoped differently? request scoped, other…

  1. Phase III - Fix up the model of optional dependencies

  • I think that optional dependencies should be implemented on top of the existing object using composition. Having code doing ‘if self.dep’ is a bad smell.

  • e.g. The optional thingee_api dependency in keystone.assignment.core.Manager could be implemented as a wrapper around the backend using a decorator pattern.

  • Other optional dependencies may have to be handled differently


  • We could go all in using a complicated framework with too many features.

  • We could do nothing and continue to grow the magic.

Security Impact

None. This spec is about internal modifications to the code structure.

Notifications Impact

None. This spec is about internal modifications to the code structure.

Other End User Impact

None. This spec is about internal modifications to the code structure.

Performance Impact

None. This spec is about internal modifications to the code structure.

Other Deployer Impact

None. This spec is about internal modifications to the code structure.

Developer Impact

This changes the way the manager objects are constructed.



Primary assignee:


Work Items

  • Update objects to expect dependencies passed into the constructor

  • Update documentation



Documentation Impact

The developer documentation will need to be updated to remove/update code samples that show keystone.common.dependency usage. This is primarily in doc/source/extension_development.rst.


Related items: