Example #1
0
 def test_simple_replace(self):
     """Replace parameter references in simple template with argument values.
     """
     for filename in [TEMPLATE_YAML_FILE, TEMPLATE_JSON_FILE]:
         template = DefaultTemplateLoader().load(filename)
         arguments = {
             'code':
             TemplateArgument(parameter=template.get_parameter('code'),
                              value=FileHandle('code/helloworld.py')),
             'names':
             TemplateArgument(parameter=template.get_parameter('names'),
                              value=FileHandle('data/list-of-names.txt')),
             'sleeptime':
             TemplateArgument(parameter=template.get_parameter('sleeptime'),
                              value=10)
         }
         spec = tmpl.replace_args(spec=template.workflow_spec,
                                  arguments=arguments,
                                  parameters=template.parameters)
         assert spec['inputs']['files'][0] == 'helloworld.py'
         assert spec['inputs']['files'][1] == 'data/names.txt'
         assert spec['inputs']['parameters'][
             'helloworld'] == 'code/helloworld.py'
         assert spec['inputs']['parameters'][
             'inputfile'] == 'data/names.txt'
         assert spec['inputs']['parameters']['sleeptime'] == 10
         assert spec['inputs']['parameters']['waittime'] == 5
Example #2
0
 def test_serialization(self):
     """Test serialization of workflow templates."""
     template = TemplateHandle(
         identifier='ABC',
         base_dir='XYZ',
         workflow_spec=dict(),
         parameters=[
             TemplateParameter(pd.parameter_declaration('A')),
             TemplateParameter(
                 pd.parameter_declaration('B', data_type=pd.DT_LIST)),
             TemplateParameter(pd.parameter_declaration('C', parent='B'))
         ])
     doc = DefaultTemplateLoader().to_dict(template)
     parameters = DefaultTemplateLoader().from_dict(doc).parameters
     assert len(parameters) == 3
     assert 'A' in parameters
     assert 'B' in parameters
     assert len(parameters['B'].children) == 1
     template = DefaultTemplateLoader().from_dict(doc)
     assert template.identifier == 'ABC'
     # The base directory is not materialized
     assert template.base_dir is None
     # Invalid resource descriptor serializations
     with pytest.raises(err.InvalidTemplateError):
         ResourceDescriptor.from_dict(dict())
     with pytest.raises(err.InvalidTemplateError):
         ResourceDescriptor.from_dict({LABEL_ID: 'A', 'noname': 'B'})
Example #3
0
 def test_nested_parameters(self):
     """Test proper nesting of parameters for DT_LIST and DT_RECORD."""
     # Create a new TemplateHandle with an empty workflow specification and
     # a list of six parameters (one record and one list)
     template = DefaultTemplateLoader().from_dict(
         {
             loader.LABEL_WORKFLOW:
             dict(),
             loader.LABEL_PARAMETERS: [
                 pd.parameter_declaration('A'),
                 pd.parameter_declaration('B', data_type=pd.DT_RECORD),
                 pd.parameter_declaration('C', parent='B'),
                 pd.parameter_declaration('D', parent='B'),
                 pd.parameter_declaration('E', data_type=pd.DT_LIST),
                 pd.parameter_declaration('F', parent='E'),
             ]
         },
         validate=True)
     # Parameters 'A', 'C', 'D', and 'F' have no children
     for key in ['A', 'C', 'D', 'F']:
         assert not template.get_parameter(key).has_children()
     # Parameter 'B' has two children 'C' and 'D'
     b = template.get_parameter('B')
     assert b.has_children()
     assert len(b.children) == 2
     assert 'C' in [p.identifier for p in b.children]
     assert 'D' in [p.identifier for p in b.children]
     # Parameter 'E' has one childr 'F'
     e = template.get_parameter('E')
     assert e.has_children()
     assert len(e.children) == 1
     assert 'F' in [p.identifier for p in e.children]
 def test_add_template(self, tmpdir):
     """Test creating templates."""
     store = TemplateRepository(base_dir=str(tmpdir))
     template = store.add_template(src_dir=WORKFLOW_DIR)
     self.validate_template_handle(template)
     # Ensure that the template handle has been serialized correctly
     f = os.path.join(store.base_dir, template.identifier, TEMPLATE_FILE)
     doc = DefaultTemplateLoader().load(f)
     d = os.path.join(store.base_dir, template.identifier, STATIC_FILES_DIR)
     assert os.path.isdir(d)
     # Get template and repeat tests
     self.validate_template_handle(store.get_template(template.identifier))
     store = TemplateRepository(base_dir=str(tmpdir))
     self.validate_template_handle(store.get_template(template.identifier))
     # Add template with JSON specification file
     template = store.add_template(src_dir=WORKFLOW_DIR,
                                   template_spec_file=JSON_SPEC)
     self.validate_template_handle(template)
     # Unknown template error
     with pytest.raises(err.UnknownTemplateError):
         store.get_template('unknown')
     # Errors when specifying wrong parameter combination
     with pytest.raises(ValueError):
         store.add_template()
     with pytest.raises(ValueError):
         store.add_template(src_dir=WORKFLOW_DIR, src_repo_url=WORKFLOW_DIR)
     # Load templates with erroneous specifications
     with pytest.raises(err.InvalidTemplateError):
         store.add_template(src_dir=WORKFLOW_DIR,
                            template_spec_file=ERR_SPEC)
     # Error when cloning invalid repository from GitHub
     with pytest.raises(err.InvalidTemplateError):
         store.add_template(
             src_repo_url='https://github.com/reanahub/reana-demo-helloworld'
         )
Example #5
0
 def test_sort(self):
     """Test the sort functionality of the template list_parameters method.
     """
     # Create a new TemplateHandle with an empty workflow specification and
     # a list of five parameters
     template = DefaultTemplateLoader().from_dict(
         {
             loader.LABEL_WORKFLOW:
             dict(),
             loader.LABEL_PARAMETERS: [
                 pd.parameter_declaration('A', index=1),
                 pd.parameter_declaration('B'),
                 pd.parameter_declaration('C'),
                 pd.parameter_declaration('D', index=2),
                 pd.parameter_declaration('E', index=1)
             ]
         },
         validate=True)
     # Get list of sorted parameter identifier from listing
     keys = [p.identifier for p in template.list_parameters()]
     assert keys == ['B', 'C', 'A', 'E', 'D']
Example #6
0
 def test_duplicate_id(self):
     """Ensure that exception is raised if parameter identifier are not
     unique.
     """
     with pytest.raises(err.InvalidTemplateError):
         DefaultTemplateLoader().from_dict(
             {
                 loader.LABEL_WORKFLOW:
                 dict(),
                 loader.LABEL_PARAMETERS: [
                     pd.parameter_declaration('A', index=1),
                     pd.parameter_declaration('B'),
                     pd.parameter_declaration('C'),
                     pd.parameter_declaration('A', index=2),
                     pd.parameter_declaration('E', index=1)
                 ]
             },
             validate=True)
 def test_expand_parameters(self):
     """Test parameter expansion."""
     template = DefaultTemplateLoader().load(TEMPLATE_FILE)
     arguments = {
         'code': TemplateArgument(
             parameter=template.get_parameter('code'),
             value=FileHandle(filepath='code/runme.py')
         ),
         'names': TemplateArgument(
             parameter=template.get_parameter('names'),
             value=FileHandle(filepath='data/myfriends.txt')
         ),
         'sleeptime': TemplateArgument(
             parameter=template.get_parameter('sleeptime'),
             value=11
         ),
         'waittime': TemplateArgument(
             parameter=template.get_parameter('waittime'),
             value=22
         )
     }
     commands = mp.get_commands(template=template, arguments=arguments)
     CMDS = [
         'python "runme.py" --inputfile "data/names.txt" --outputfile "results/greetings.txt" --sleeptime 11',
         'wait 22',
         'python "code/eval.py" --inputfile "results/greetings.txt" --outputfile results.json'
     ]
     assert commands == CMDS
     # Default values
     arguments = {
         'names': TemplateArgument(
             parameter=template.get_parameter('names'),
             value=FileHandle(filepath='data/myfriends.txt')
         )
     }
     commands = mp.get_commands(template=template, arguments=arguments)
     CMDS = [
         'python "code/helloworld.py" --inputfile "data/names.txt" --outputfile "results/greetings.txt" --sleeptime 10',
         'wait 5',
         'python "code/eval.py" --inputfile "results/greetings.txt" --outputfile results.json'
     ]
     assert commands == CMDS
     # Error cases
     del template.workflow_spec['inputs']['parameters']['inputfile']
     with pytest.raises(err.InvalidTemplateError):
         mp.get_commands(template=template, arguments=arguments)
Example #8
0
    def __init__(self,
                 base_dir,
                 loader=None,
                 filenames=None,
                 suffixes=None,
                 id_func=None,
                 max_attempts=DEFAULT_MAX_ATTEMPTS):
        """Initialize the base directory where templates are maintained. The
        optional identifier function is used to generate unique template
        identifier. By default, short identifier are used.

        Uses the cross product of file names and suffixes to look for a
        template specification file when adding a new template.

        Parameters
        ----------
        base_dir: string
            Base directory for templates
        loader: benchtmpl.workflow.template.loader.TemplateLoader, optional
            Loader for reading and writing template specification files
        filenames: list(string)
            List of file names for templates files
        suffixes: list(string)
            List of recognized file suffies for template files
        id_func: func, optional
            Function to generate template folder identifier
        max_attempts: int, optional
            Maximum number of attempts to create a unique folder for a new
            workflow template
        """
        self.base_dir = os.path.abspath(base_dir)
        self.loader = loader if not loader is None else DefaultTemplateLoader()
        self.filenames = filenames if not filenames is None else [
            'template', 'workflow'
        ]
        self.suffixes = suffixes if not suffixes is None else [
            '.yml', '.yaml', '.json'
        ]
        self.id_func = id_func if not id_func is None else util.get_short_identifier
        self.max_attempts = max_attempts
        # Create the base directory if it does not exist
        util.create_dir(self.base_dir)
Example #9
0
 def test_get_parameter_references(self):
     """Test function to get all parameter references in a workflow
     specification."""
     spec = {
         'input':
         ['A', '$[[X]]', {
             'B': {
                 'C': '$[[Y]]',
                 'D': [123, '$[[Z]]']
             }
         }],
         'E': {
             'E': 'XYZ',
             'F': 23,
             'G': '$[[W]]'
         },
         'F': '$[[U]]',
         'G': ['$[[V]]', 123]
     }
     refs = tmpl.get_parameter_references(spec)
     assert refs == set(['U', 'V', 'W', 'X', 'Y', 'Z'])
     # If given parameter set as argument the elements in that set are part
     # of the result
     para = set(['A', 'B', 'X'])
     refs = tmpl.get_parameter_references(spec, parameters=para)
     assert refs == set(['A', 'B', 'U', 'V', 'W', 'X', 'Y', 'Z'])
     # Error if specification contains nested lists
     with pytest.raises(err.InvalidTemplateError):
         tmpl.get_parameter_references({
             'input': [
                 'A', ['$[[X]]'], {
                     'B': {
                         'C': '$[[Y]]',
                         'D': [123, '$[[Z]]']
                     }
                 }
             ]
         })
     # Error when loading specification that references undefined parameter
     with pytest.raises(err.UnknownParameterError):
         template = DefaultTemplateLoader().load(TEMPLATE_ERR,
                                                 validate=True)
Example #10
0
 def test_nested_parse(self):
     """Test parsing arguments for a nested parameter declaration."""
     template = DefaultTemplateLoader().from_dict(
         {
             tmpl.LABEL_WORKFLOW:
             dict(),
             tmpl.LABEL_PARAMETERS: [
                 pd.parameter_declaration('A', data_type=pd.DT_INTEGER),
                 pd.parameter_declaration('B', data_type=pd.DT_RECORD),
                 pd.parameter_declaration(
                     'C', data_type=pd.DT_DECIMAL, parent='B'),
                 pd.parameter_declaration('D',
                                          data_type=pd.DT_STRING,
                                          parent='B',
                                          required=False),
                 pd.parameter_declaration(
                     'E', data_type=pd.DT_LIST, required=False),
                 pd.parameter_declaration(
                     'F', data_type=pd.DT_INTEGER, parent='E'),
                 pd.parameter_declaration('G',
                                          data_type=pd.DT_DECIMAL,
                                          parent='E',
                                          required=False)
             ]
         },
         validate=True)
     params = template.parameters
     # Without values for list parameters
     args = values.parse_arguments(arguments={
         'A': 10,
         'B': {
             'C': 12.3
         }
     },
                                   parameters=params,
                                   validate=True)
     assert len(args) == 2
     assert not args['B'].value.get('C') is None
     assert args['B'].value.get('D') is None
     assert len(args['B'].value) == 1
     assert args['B'].value.get('C').value == 12.3
     # With list arguments
     args = values.parse_arguments(arguments={
         'A':
         10,
         'B': {
             'C': 12.3,
             'D': 'ABC'
         },
         'E': [{
             'F': 1
         }, {
             'F': 2
         }, {
             'F': 3,
             'G': 0.9
         }]
     },
                                   parameters=params,
                                   validate=True)
     assert len(args) == 3
     assert not args['B'].value.get('C') is None
     assert not args['B'].value.get('D') is None
     assert len(args['B'].value) == 2
     assert len(args['E'].value) == 3
     for arg in args['E'].value:
         if arg.get('F').value < 3:
             assert len(arg) == 1
         else:
             assert len(arg) == 2
     # Error cases
     with pytest.raises(ValueError):
         values.parse_arguments(arguments={
             'A': 10,
             'B': [{
                 'C': 12.3
             }]
         },
                                parameters=params,
                                validate=True)