def test_single_layer_pipeline_composite_descent():
    @solid(config=int)
    def return_int(context):
        return context.solid_config

    @composite_solid
    def return_int_passthrough():
        return_int()

    @pipeline
    def return_int_pipeline_passthrough():
        return_int_passthrough()

    solid_config_dict = composite_descent(
        return_int_pipeline_passthrough,
        {'return_int_passthrough': {'solids': {'return_int': {'config': 34}}}},
    )

    handle = 'return_int_passthrough.return_int'
    assert solid_config_dict[handle].config == 34

    result = execute_pipeline(
        return_int_pipeline_passthrough,
        {'solids': {'return_int_passthrough': {'solids': {'return_int': {'config': 34}}}},},
    )

    assert result.success
    assert result.result_for_handle(handle).output_value() == 34
def test_double_nested_input_via_config_mapping():
    @lambda_solid
    def number(num):
        return num

    @composite_solid(
        config_fn=lambda context, _: {'number': {'inputs': {'num': {'value': 4}}}}, config={}
    )
    def wrap_solid():  # pylint: disable=unused-variable
        return number()

    @composite_solid
    def double_wrap(num):
        number(num)
        return wrap_solid()

    @pipeline
    def wrap_pipeline_double_nested_input():
        return double_wrap()

    solid_handle_dict = composite_descent(
        wrap_pipeline_double_nested_input, {'double_wrap': {'inputs': {'num': {'value': 2}}}}
    )
    assert solid_handle_dict['double_wrap.wrap_solid.number'].inputs == {'num': {'value': 4}}
    assert solid_handle_dict['double_wrap'].inputs == {'num': {'value': 2}}

    result = execute_pipeline(
        wrap_pipeline_double_nested_input,
        {'solids': {'double_wrap': {'inputs': {'num': {'value': 2}}}}},
    )
    assert result.success
Exemplo n.º 3
0
    def build(pipeline, environment_dict=None, run_config=None):
        from dagster.core.types.config.evaluator.composite_descent import composite_descent
        from dagster.core.types.config.evaluator.validate import process_config

        check.inst_param(pipeline, 'pipeline', PipelineDefinition)
        environment_dict = check.opt_dict_param(environment_dict, 'environment_dict')
        run_config = check.opt_inst_param(run_config, 'run_config', IRunConfig, default=RunConfig())

        mode = run_config.mode or pipeline.get_default_mode_name()
        environment_type = create_environment_type(pipeline, mode)

        config_evr = process_config(environment_type, environment_dict)
        if not config_evr.success:
            raise DagsterInvalidConfigError(
                'Error in config for pipeline {}'.format(pipeline.name),
                config_evr.errors,
                environment_dict,
            )

        config_value = config_evr.value

        solid_config_dict = composite_descent(pipeline, config_value.get('solids', {}), run_config)

        return EnvironmentConfig(
            solids=solid_config_dict,
            execution=ExecutionConfig.from_dict(config_value.get('execution')),
            storage=StorageConfig.from_dict(config_value.get('storage')),
            loggers=config_value.get('loggers'),
            original_config_dict=environment_dict,
            resources=config_value.get('resources'),
        )
def test_direct_composite_descent_with_error():
    @composite_solid(
        config={'override_str': Field(int)},
        config_fn=lambda _, cfg: {'layer2': {'config': cfg['override_str']}},
    )
    def wrap_coerce_to_wrong_type():
        return scalar_config_solid.alias('layer2')()

    @composite_solid(
        config={'nesting_override': Field(int)},
        config_fn=lambda _, cfg: {'layer1': {'config': {'override_str': cfg['nesting_override']}}},
    )
    def nesting_wrap_wrong_type_at_leaf():
        return wrap_coerce_to_wrong_type.alias('layer1')()

    @pipeline
    def wrap_pipeline_with_error():
        return nesting_wrap_wrong_type_at_leaf.alias('layer0')()

    with pytest.raises(DagsterInvalidConfigError) as exc_info:
        composite_descent(
            wrap_pipeline_with_error, {'layer0': {'config': {'nesting_override': 214}}}
        )

    assert 'In pipeline wrap_pipeline_with_error at stack layer0:layer1:' in str(exc_info.value)

    assert (
        'Solid "layer1" with definition "wrap_coerce_to_wrong_type" has a configuration error.'
        in str(exc_info.value)
    )

    assert (
        '''Error 1: Type failure at path "root:layer2:config" on type "String". '''
        '''Value at path root:layer2:config is not valid. Expected "String".'''
        in str(exc_info.value)
    )
def test_single_layer_pipeline_hardcoded_config_mapping():
    @solid(config=int)
    def return_int(context):
        return context.solid_config

    @composite_solid(config={}, config_fn=lambda _ctx, _cfg: {'return_int': {'config': 35}})
    def return_int_hardcode_wrap():
        return_int()

    @pipeline
    def return_int_hardcode_wrap_pipeline():
        return_int_hardcode_wrap()

    solid_config_dict = composite_descent(return_int_hardcode_wrap_pipeline, {})

    assert solid_config_dict['return_int_hardcode_wrap.return_int'].config == 35
def test_single_solid_pipeline_composite_descent():
    @solid(config=int)
    def return_int(context):
        return context.solid_config

    @pipeline
    def return_int_pipeline():
        return_int()

    solid_config_dict = composite_descent(return_int_pipeline, {'return_int': {'config': 3}})

    assert solid_config_dict['return_int'].config == 3

    result = execute_pipeline(return_int_pipeline, {'solids': {'return_int': {'config': 3}}})

    assert result.success
    assert result.result_for_solid('return_int').output_value() == 3
def test_nested_input_via_config_mapping():
    @solid
    def add_one(_, num):
        return num + 1

    @composite_solid(
        config={}, config_fn=lambda _cxt, _cfg: {'add_one': {'inputs': {'num': {'value': 2}}}}
    )
    def wrap_add_one():
        add_one()

    @pipeline
    def wrap_add_one_pipeline():
        wrap_add_one()

    solid_config_dict = composite_descent(wrap_add_one_pipeline, {})
    assert solid_config_dict['wrap_add_one.add_one'].inputs == {'num': {'value': 2}}

    result = execute_pipeline(wrap_add_one_pipeline)
    assert result.success
    assert result.result_for_handle('wrap_add_one.add_one').output_value() == 3
def test_single_layer_pipeline_computed_config_mapping():
    @solid(config=int)
    def return_int(context):
        return context.solid_config

    def _config_fn(_, cfg):
        return {'return_int': {'config': cfg['number'] + 1}}

    @composite_solid(config={'number': int}, config_fn=_config_fn)
    def return_int_plus_one():
        return_int()

    @pipeline
    def return_int_hardcode_wrap_pipeline():
        return_int_plus_one()

    solid_config_dict = composite_descent(
        return_int_hardcode_wrap_pipeline, {'return_int_plus_one': {'config': {'number': 23}}}
    )

    assert solid_config_dict['return_int_plus_one.return_int'].config == 24
def test_mix_layer_computed_mapping():
    @solid(config=int)
    def return_int(context):
        return context.solid_config

    @composite_solid(
        config={'number': int},
        config_fn=lambda _, cfg: {'return_int': {'config': cfg['number'] + 1}},
    )
    def layer_three_wrap():
        return_int()

    def _layer_two_double_wrap_cfg_fn(_, cfg):
        if cfg['inject_error']:
            return {'layer_three_wrap': {'config': {'number': 'a_string'}}}
        else:
            return {'layer_three_wrap': {'config': {'number': cfg['number'] + 1}}}

    @composite_solid(
        config={'number': int, 'inject_error': bool}, config_fn=_layer_two_double_wrap_cfg_fn
    )
    def layer_two_double_wrap():
        layer_three_wrap()

    @composite_solid
    def layer_two_passthrough():
        return_int()

    @composite_solid
    def layer_one():
        layer_two_passthrough()
        layer_two_double_wrap()

    @pipeline
    def layered_config():
        layer_one()

    solid_config_dict = composite_descent(
        layered_config,
        {
            'layer_one': {
                'solids': {
                    'layer_two_passthrough': {'solids': {'return_int': {'config': 234}}},
                    'layer_two_double_wrap': {'config': {'number': 5, 'inject_error': False}},
                }
            }
        },
    )

    assert solid_config_dict['layer_one.layer_two_passthrough.return_int'].config == 234
    # this passed through both config fns which each added one
    assert (
        solid_config_dict['layer_one.layer_two_double_wrap.layer_three_wrap.return_int'].config == 7
    )

    with pytest.raises(DagsterInvalidConfigError) as exc_info:
        composite_descent(
            layered_config,
            {
                'layer_one': {
                    'solids': {
                        'layer_two_passthrough': {'solids': {'return_int': {'config': 234}}},
                        'layer_two_double_wrap': {'config': {'number': 234, 'inject_error': True}},
                    }
                }
            },
        )

    assert 'Solid "layer_two_double_wrap" with definition "layer_two_double_wrap"' in str(
        exc_info.value
    )

    assert (
        'Error 1: Type failure at path "root:layer_three_wrap:config:number" on type "Int". '
        'Value at path root:layer_three_wrap:config:number is not valid. Expected "Int"'
    ) in str(exc_info.value)

    result = execute_pipeline(
        layered_config,
        {
            'solids': {
                'layer_one': {
                    'solids': {
                        'layer_two_passthrough': {'solids': {'return_int': {'config': 55}}},
                        'layer_two_double_wrap': {'config': {'number': 7, 'inject_error': False}},
                    }
                }
            },
        },
    )

    assert (
        result.result_for_handle('layer_one.layer_two_passthrough.return_int').output_value() == 55
    )
    assert (
        result.result_for_handle(
            'layer_one.layer_two_double_wrap.layer_three_wrap.return_int'
        ).output_value()
        == 9
    )