Skip to content
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

Dependency Injection #127

Closed
mottosso opened this issue Oct 28, 2014 · 3 comments
Closed

Dependency Injection #127

mottosso opened this issue Oct 28, 2014 · 3 comments
Labels
Milestone

Comments

@mottosso
Copy link
Member

Hi all,

This is an idea for simplified plug-in development.

Goal

To merge process_context and process_instance into a single process method.

# From

class MySelector(pyblish.api.Selector):
    def process_instance(self, instance):
        # perform operations on `instance`

# To

class MySelector(pyblish.api.Selector):
    def process(self, instance):
        # perform operations on `instance`

Architecture

The question then is:

"How do you choose whether to process the instance, or context, or both?"

The answer is:

"Dependency injection"

I picked it up from AngularJS, in which arguments to a function is "injected" based on what is being asked for. For example.

def process(instance):
    assert isinstance(instance, pyblish.api.Instance)  # True

def process(context):
    assert isinstance(context, pyblish.api.Context)  # True

def process(context, instance):
    assert isinstance(instance, pyblish.api.Instance)  # True
    assert isinstance(context, pyblish.api.Context)  # True

Basically, you get what you ask for. This magic is called Dependency Injection, or DI. And is pretty slick.

Here's an implementation.

# Dependency injection demonstration

import inspect


def process(instance):
    """This is the function in which we ask for dependencies, instance in this case"""
    print instance


class Instance(object):
    pass


class Context(object):
    pass


# Available dependencies for injection
variables = {'instance': Instance(),
             'context': Context()}

# What is being asked for?
arguments = inspect.getargspec(process)[0]

# Based on the names of the arguments being asked for,
# inject the corresponding dependencies.
inject = []
for argument in arguments:
    if argument in variables:
        inject.append(variables[argument])

process(*inject)

# prints Instance object

Result

Cleaner code. If we need neither Instance nor Context, we are also free to do:

def process():
    pass

In which case no dependencies are injected (which would effectively never get called).

See here for more information.

Roadmap

I'll implement this in our current release as it doesn't break any of our current functionality, but instead augments what can already be done. I'd suggest we keep the current methods, process_instance et al., around for the time being and slowly progress into the new behaviour. If, of course, it turns out to be favourable.

Let me know what you think!

Best,
Marcus

@mottosso mottosso added this to the 1.0 Beta milestone Oct 28, 2014
@tokejepsen
Copy link
Member

nice work! should clear up any confusion there might be when writing
plugins.

@mottosso
Copy link
Member Author

Yeah, hope so! I'm sure there will be other issues with it, but apparently DI is fairly common, especially in IoC frameworks like ours. It's also good for attracting crowds. "Dependency injection? Woaah" ;)

@mottosso
Copy link
Member Author

mottosso commented Jun 4, 2015

Added to 1.1

@mottosso mottosso closed this as completed Jun 4, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants