argoproj-labs/argo-python-dsl

Class inheritance

process0 opened this issue · 7 comments

It would be useful to have class inheritance on Workflow/WorkflowTemplates objects so that common components could be reused. This may not be as big of a requirement if #18 is implemented as those components could be referenced.

Ideas?

I've inherited from the Workflow class and run the inheriting workflows without problems.
`
argo 2.11.7
python 3.8.5

argo-workflows==4.0.1
argo-workflows-dsl==0.4.0
`

Inheritance doesn't solve #18 though, and handles a completely different use case. Referencing templates/workflows is one thing, and a whole different thing is using inheritance to more succinctly and correctly define them in code.

I've now come upon your problem as well - you can't inherit @templates... So this still stands.

I think I've located the problem - in WorkflowMeta, the code collects the props just from the current class and not from all bases of the current class. Changing this to collecting all of them will probably yield a good result.

I've ran into a similar problem that may be worth considering- I didn't want to inherit templates, but I wanted use the scope decorator on functions in the parent class and make them available to closures on the child classes. I am currently using a custom metaclass.

That's really interesting, how did you do that? Can you share the code?

About metaclasses - I'd like to steer clear of changing the metaclass or in fact writing any metaclass code if possible...

And about templating inherited methods - I think the better approach would be to override the parent's method and declare your override as a closure, even if it's just calling the parent. Unlike templates, closures are ad-hoc compiled so explicitly overriding provides clearer resolution for the code AND brevity IMO.

Come to think of it... The order in which templates are defined on a class defines the order of steps in the workflow - and the order they appear in the child class if inherited from the parent is deterministic, so this is not a bad idea at all. It's 100% implicit, but deterministic. The problem is what happens when you mix and match things with DAGs - then you'd have to override the parent class' methods and redefine the ordering yourself using template declarations.

Looking back at my code, it is not generalizable- it is using a metaclass on the parent to dynamically call scope() on preset functions on the base class when the child class is created. I agree, your solution of overriding the parent methods would probably be clearer.

I've tried reusing methods in a class hierarchy, and it seemed that whatever I tag with @template runs only once. Upon investigation of the

class template(Spec):
source code, I've found it extends Spec, which is compiled once (see
def model(self, spec: T):
and
self._spec.model = model
). This can't be changed without changing how the model property is handled in Workflow at the source code level, as it uses self.__compiled_model under the hood.
self.spec = _compile(self.spec)

I've overcome all this by dynamically generating a new class inheriting form the class I actually wanted to use every time I want to submit a new instance of the workflow. I had to redeclare things like entrypoint and service_account_name in the dynamically generated class too. At least there's a workaround!