ecFlow's documentation is now on readthedocs!

Python’s object-oriented design features allow considerable flexibility in how we design and structure our suite definition.

Each suite will have a different set of forces that determine how it should be designed.

Let's consider how we would design the tutorial examples in a more object-oriented manner. We start with some design criteria we must meet.

  • The default variables (ECF_HOME, etc) must be configurable and independent of the suites
  • New suites must enable automatic job creation checking
  • We need to write out definition as a separate file
  • New suites should be able to re-use the “boilerplate” code defined by the above requirements

Here is a possible design, that uses inheritance and the template design pattern:

import os
import ecflow  

class DefaultVariables(object):
    """Provide the setup variables for each suite"""
    def add_to(self, node):
        """Adds ECF_INCLUDE,ECF_HOME to the input node"""
        node.add_variable("ECF_INCLUDE", os.path.join(os.getenv("HOME"),  "course"))
        node.add_variable("ECF_HOME", os.path.join(os.getenv("HOME"),  "course"))


class BaseSuiteBuilder(object):
    """Abstract class. Add default variables to suite and enable job creation 
 checking for any derived suite
 """
    def __init__(self, default_variables):
        self.defs = ecflow.Defs()
        # use derived class name as suite name
        self.suite = self.defs.add_suite(type(self).__name__) 
        default_variables.add_to(self.suite)
        
    def _build_definition_hook(self):
        """Derived suite should override this function to build the suite
 Should not be called explicitly. How could we enforce this ?
 """
        pass
    
    def setup(self):
        """Template/skeleton function. 
 Provides common algorithm for *all* derivatives
 Uses Hollywood principle.
 """
        
        # Build the suite
        self._build_definition_hook()
        
        # check job creation. Could use an assert
        job_check_result = self.defs.check_job_creation()
        if len(job_check_result) != 0:
            print("Job creation failed\n" + job_check_result)
 
        # Check trigger expressions and limit references"
        print(self.defs.check())
                   
        # Allows definition creation to be separated from the load
        # Use the class name as the name of the definition file
        self.defs.save_as_defs( type(self).__name__ + ".def")
        
        
class SuiteBuilder(BaseSuiteBuilder):
    """My example suite. Generates SuiteBuilder.def"""
    def __init__(self, default_variables):
        BaseSuiteBuilder.__init__(self, default_variables)
        
    def _build_definition_hook(self):
        f1 = self.suite.add_family("family")
        f1.add_task("task1")           
        f1.add_task("task2") 
        

class TestSuite(BaseSuiteBuilder):
    """My test suite. Generates TestSuite.def"""
    def __init__(self, default_variables):
        BaseSuiteBuilder.__init__(self, default_variables)
        
    def _build_definition_hook(self):
        self.suite.add_task("task1")


if __name__ == "__main__":

    my_suite = SuiteBuilder(DefaultVariables())
    my_suite.setup()

    my_test_suite = TestSuite(DefaultVariables())
    my_test_suite.setup()
            

1 Comment

  1. It is possible to use name mangling to achieve a certain level of hiding of _build_definition_hook from the public API:

    class BaseSuiteBuilder(object):
        """Abstract class. Add default variables to suite and enable job creation 
     checking for any derived suite
     """
        def __init__(self, default_variables):
            self.defs = ecflow.Defs()
            # use derived class name as suite name
            self.suite = self.defs.add_suite(type(self).__name__) 
            default_variables.add_to(self.suite)
            
        def __build_definition_hook(self):
            """Derived suite should override this function to build the suite
     Should not be called explicitly. How could we enforce this ? => The leading
     double underscore will transform the name of the method to _BaseSuiteBuilder__build_definition_hook
     for public "users"
     """
            pass
        
        def setup(self):
            """Template/skeleton function. 
     Provides common algorithm for *all* derivatives
     Uses Hollywood principle.
     """
            
            # Build the suite
            # the name is still visible to the class members as is
            self.__build_definition_hook()
            
            # check job creation. Could use an assert
            job_check_result = self.defs.check_job_creation()
            if len(job_check_result) != 0:
                print("Job creation failed\n" + job_check_result)
     
            # Check trigger expressions and limit references"
            print(self.defs.check())
                       
            # Allows definition creation to be separated from the load
            # Use the class name as the name of the definition file
            self.defs.save_as_defs( type(self).__name__ + ".def")