Test Failed
Pull Request — master (#888)
by Yousef
04:45
created

savu.core.iterate_plugin_group_utils   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 163
rs 10
c 0
b 0
f 0
wmc 16

6 Functions

Rating   Name   Duplication   Size   Complexity  
A check_if_end_plugin_in_iterate_group() 0 12 2
A create_clone() 0 6 1
A setup_extra_plugin_data_padding() 0 21 3
A enable_iterative_loop() 0 65 4
A check_if_in_iterative_loop() 0 34 5
A shift_plugin_index() 0 16 1
1
from savu.data.data_structures.plugin_data import PluginData
2
3
4
def enable_iterative_loop(setup_fn):
5
    '''
6
    Decorator that can be applied to a plugin's setup() method. Doing so
7
    modifies the plugin's setup slightly to work correctly when at the end of an
8
    iterative loop, by setting up the relevant extra output dataset.
9
    '''
10
    def wrapper(*args, **kwargs):
11
        # run the plugin's original setup() method
12
        setup_fn(*args, **kwargs)
13
        plugin = args[0]
14
15
        if check_if_end_plugin_in_iterate_group(plugin.exp):
16
            # do the other setup required for the plugin to be the end plugin of
17
            # an iterative group
18
19
            in_dataset, out_dataset = plugin.get_datasets()
20
            in_pData, out_pData = plugin.get_plugin_datasets()
21
22
            try:
23
                max_frames = plugin.get_max_frames()
24
            except AttributeError:
25
                # the plugin has no get_max_frames() method, so assume it to be
26
                # 'single
27
                max_frames = 'single'
28
29
            # create the cloned dataset
30
            create_clone(out_dataset[1], out_dataset[0])
31
32
            # this is the pattern that was used for the "original" output data
33
            # for the end plugin
34
            out_pattern = out_pData[0].get_pattern_name()
35
            # set the pattern for the PluginData objects associated with the
36
            # newly created cloned dataset
37
            out_pData[1].plugin_data_setup(out_pattern, max_frames)
38
39
            try:
40
                iterate_plugin_group = check_if_in_iterative_loop(plugin.exp)
41
                start_plugin = iterate_plugin_group.start_plugin
42
                start_plugin_in_pData, start_plugin_out_pData = \
43
                    start_plugin.get_plugin_datasets()
44
                # this is the pattern that was used for the input data for the
45
                # start plugin
46
                in_pattern = start_plugin_in_pData[0].get_pattern_name()
47
                # this is the padding that is going to be applied to the input
48
                # data for the start plugin
49
                in_padding = start_plugin_in_pData[0].padding
50
51
                # create PluginData object for original and cloned Data objects,
52
                # that have the pattern of the start plugin, and append to the
53
                # start plugin's 'plugin_in_datasets'
54
                orig_start_pData = PluginData(out_dataset[0], start_plugin)
55
                orig_start_pData.padding = in_padding
56
                orig_start_pData.plugin_data_setup(in_pattern, 'single')
57
                start_plugin.parameters['plugin_in_datasets'].append(orig_start_pData)
58
                clone_start_pData = PluginData(out_dataset[1], start_plugin)
59
                clone_start_pData.padding = in_padding
60
                clone_start_pData.plugin_data_setup(in_pattern, 'single')
61
                start_plugin.parameters['plugin_in_datasets'].append(clone_start_pData)
62
                # "re-finalise" the plugin datasets for the start plugin, now
63
                # that these new PluginData obejcts have been added
64
                start_plugin._finalise_plugin_datasets()
65
            except AttributeError as e:
66
                print('In plugin setup, will not create new PluginData objects')
67
68
    return wrapper
69
70
def setup_extra_plugin_data_padding(padding_fn):
71
    """
72
    Decorator that can be applied to a filter plugin's set_filter_padding()
73
    method. Doing so modifies/extends the filter plugin's padding function
74
    slightly to also set the padding for the additional PluginData objects that
75
    are created when an iterative loop is defined.
76
    """
77
78
    def wrapper(*args, **kwargs):
79
        # run the plugin's original set_filter_padding() method
80
        padding_fn(*args, **kwargs)
81
        plugin = args[0]
82
83
        if check_if_end_plugin_in_iterate_group(plugin.exp):
84
            # check for any padding on the original output data, and apply it to
85
            # the cloned data
86
            in_pData, out_pData = plugin.get_plugin_datasets()
87
            if out_pData[0].padding is not None:
88
                out_pData[1].padding = out_pData[0].padding
89
90
    return wrapper
91
92
def create_clone(clone, data):
93
    '''
94
    Create a clone of a Data object
95
    '''
96
    clone.create_dataset(data)
97
    clone.data_info.set('clone', data.get_name())
98
99
def check_if_in_iterative_loop(exp):
100
    '''
101
    Inspect the metadata inside the Experiment object to determine if current
102
    processing is inside an iterative loop
103
    '''
104
    try:
105
        current_plugin_index = exp.meta_data.get('nPlugin')
106
        for group in exp.meta_data.get('iterate_groups'):
107
            start_index = shift_plugin_index(exp, group.start_index)
108
            end_index = shift_plugin_index(exp, group.end_index)
109
            if start_index <= current_plugin_index and \
110
                end_index >= current_plugin_index:
111
                return group
112
113
        # never hit an instance of IteratePluginGroup where the current plugin
114
        # index was within the start and end plugin indices, so processing is
115
        # not inside an iterative loop
116
        return None
117
    except AttributeError as e:
118
        # TODO: Currently, an AttributeError only occurs inside two framework
119
        # tests:
120
        # - astra_multiple_parameter_test.py
121
        # - multiple_parameter_test.py
122
        # due to the exp variable still being None (ie, when this check of if
123
        # the processing is in an iterative loop or not occurs, the Experiment
124
        # object hasn't yet been set in these tests).
125
        #
126
        # These framework tests should be investigated to better understand:
127
        # - in what cases the Experiment object isn't yet set when running Savu
128
        # - if this check can occur elsewhere, to avoid causing those framework
129
        #   tests to fail
130
        err_str = f"Error when checking if inside an iterative loop: {e}"
131
        print(err_str)
132
        return None
133
134
def check_if_end_plugin_in_iterate_group(exp):
135
    '''
136
    Determines if the current plugin is at the end of an iterative loop
137
    '''
138
    iterate_plugin_group = check_if_in_iterative_loop(exp)
139
    if iterate_plugin_group is None:
140
        return False
141
142
    end_index = shift_plugin_index(exp, iterate_plugin_group.end_index)
143
    is_end_plugin = end_index == exp.meta_data.get('nPlugin')
144
145
    return is_end_plugin
146
147
def shift_plugin_index(exp, index):
148
    '''
149
    The indices used for plugins when editing a process list in the
150
    configurator, and the indices used by PluginRunner._run_plugin_list(),
151
    differ based on a few different things, such as:
152
    - zero-based indexing internally in Savu, but one-based indexing in the
153
      configurator
154
    - the number of loaders in the process list
155
156
    This function is for shifting the one-based plugin index as would be seen in
157
    the configurator, to the nPlugin experimental metadata value as would be
158
    seen for the same plugin in the for loop in PluginRunner._run_plugin_list().
159
    '''
160
    n_loaders = exp.meta_data.plugin_list._get_n_loaders()
161
    SHIFT_TO_ZERO_BASED = 1
162
    return index - SHIFT_TO_ZERO_BASED - n_loaders