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

Refactor Task and Subsystem dependencies #1957

Closed
jsirois opened this issue Aug 11, 2015 · 8 comments
Closed

Refactor Task and Subsystem dependencies #1957

jsirois opened this issue Aug 11, 2015 · 8 comments
Assignees

Comments

@jsirois
Copy link
Contributor

jsirois commented Aug 11, 2015

I discussed this with @benjyw offline, but the current fork in TaskBase.{global_subsystems,task_subsystems} presents a few challenges including requiring scope derivation by 3rdparties instead of the dependees themselves and not being uniform from TaskBase to Subsystem (Subsystem has a single Subsystem dependecies method). The desired 2 API changes are as follows, and the rest should flow from there:

class ExampleTask(Task):
  @classmethod
  def subsystem_dependencies(cls):
    return (
      Subsystem1,  # A global instance dependency
      Subsystem2.scoped(cls)  # A scoped instance dependency
    )

  def execute(self):
    subsystem1 = Subsystem1.instance()  # The global instance.
    subsystem2 = Subsystem2.scoped(self).instance()  # The scoped instance.

The same mix of global and scoped Subsystems can be depended on by Subsystems themselves using this API with a single dependencies method and a single instance method. The new Subsystem.scoped(optionable) method will return a new Subsystem subclass with the qualified scope baked in such that no calculation of actual scopes in-play will be needed. Instead, the Subsystem.closure can be gathered and the Subsystem (Optionable) types can be directly consulted for their full .options_scope.

@jsirois jsirois self-assigned this Aug 11, 2015
@benjyw
Copy link
Sponsor Contributor

benjyw commented Aug 12, 2015

I like this, but I would go even further:

subsystem2 = Subsystem2.scoped(self).instance() is redundant. That information was already provided in subsystem_dependencies().

It would be nice to be able to do:

  def execute(self):
    subsystem1 = self.subsystem(Subsystem1)
    subsystem2 = self.subsystem(Subsystem2)

or

  def execute(self):
    subsystem1 = Subsystem1.instance_for(self)
    subsystem2 = Subsystem2.instance_for(self)

So the author of execute() doesn't even need to think about scoping.

What do you think?

@jsirois
Copy link
Contributor Author

jsirois commented Aug 12, 2015

I'd like that too, but even further - dependency injection. The subsystems should be injected in the Task/Subsystem constructor. You shouldn't even need to ask for the thing 2x. That's a few refactors off though, and folks tend to retch at the mention of DI ao it may be controversial.

Back to your suggestion, it does not allow for mixed scope instances of the same type, but maybe its fine to not allow this. IE: no depending on both the global instance and a scoped on from the same dependee. Putting that aside and considering "normal" use cases, it does seem like Subsystem could hang on to a class-level registry of requestors -> requested Subsystem types.

@benjyw
Copy link
Sponsor Contributor

benjyw commented Aug 12, 2015

T thing I suggested could be a common shortcut, but we'd still allow your
thing.

The latter would be used in the rare case when the author of execute()
truly does need to reason about scoping.

On Tue, Aug 11, 2015 at 10:20 PM, John Sirois notifications@github.com
wrote:

I'd like that too, but even further - dependency injection. The subsystems
should be injected in the Task/Subsystem constructor. You shouldn't even
need to ask for the thing 2x. That's a few refactors off though, and folks
tend to retch at the mention of DI ao it may be controversial.

Back to your suggestion, it does not allow for mixed scope instances of
the same type, but maybe its fine to not allow this. IE: no depending on
both the global instance and a scoped on from the same dependee. Putting
that aside and considering "normal" use cases, it does seem like Subsystem
could hang on to a class-level registry of requestors -> requested
Subsystem types.


Reply to this email directly or view it on GitHub
#1957 (comment).

@jsirois
Copy link
Contributor Author

jsirois commented Aug 12, 2015

Good point - I like that.

@jsirois
Copy link
Contributor Author

jsirois commented Aug 16, 2015

As noted in pantsbuild slack #general - This change should support separating the global subsystems from those that have been scoped when examining a dependencies list.

@jsirois
Copy link
Contributor Author

jsirois commented Aug 17, 2015

@benjyw if you want this its yours. I'm still embroiled in #1998 and need to complete a fix for that for tomorrow am-ish.

Just self-assign and label in-progress. Otherwise I'll likely be bumped off hitting this until next weekend.

@benjyw
Copy link
Sponsor Contributor

benjyw commented Aug 17, 2015

It's a bit of a detour, but I do need this for my last #docfixit change, so I'll see if I can do this quick-like.

@benjyw benjyw assigned benjyw and unassigned jsirois Aug 17, 2015
benjyw pushed a commit to benjyw/pants that referenced this issue Aug 18, 2015
Introduces the concept of a subsystem client, which Task
and Subsystem mix in (this is not in Optionable because
the GlobalOptionsRegistrar must not depend on any subsystems).

All subsystem dependencies are expressed by the client by
implementing subsystem_dependencies(). That dependency
consists of a subsystem type and a scope. This generalizes
the previous concept of global and task-specific subsystem
dependencies.

Contains a temporary hack so that the old declarations still
work.  Those will be mopped up in a followup change, just
to keep this one from spiraling out too much.

Issue: pantsbuild#1957
benjyw added a commit to benjyw/pants that referenced this issue Aug 19, 2015
Introduces the concept of a subsystem client, which Task
and Subsystem mix in (this is not in Optionable because
the GlobalOptionsRegistrar must not depend on any subsystems).

All subsystem dependencies are expressed by the client by
implementing subsystem_dependencies(). That dependency
consists of a subsystem type and a scope. This generalizes
the previous concept of global and task-specific subsystem
dependencies.

Contains a temporary hack so that the old declarations still
work.  Those will be mopped up in a followup change, just
to keep this one from spiraling out too much.

Issue: pantsbuild#1957

Testing Done:
CI passes: https://travis-ci.org/pantsbuild/pants/builds/76136929.

Bugs closed: 1957

Reviewed at https://rbcommons.com/s/twitter/r/2653/
@benjyw
Copy link
Sponsor Contributor

benjyw commented Aug 19, 2015

Implemented in https://rbcommons.com/s/twitter/r/2653/.

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

No branches or pull requests

2 participants