GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Test Failed
Push — develop-v1.6.0 ( 9d5181...7efb31 )
by
unknown
04:49
created

SingleTraceDisplayMixin.print_trace_details()   F

Complexity

Conditions 15

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
dl 0
loc 39
rs 2.7451
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like SingleTraceDisplayMixin.print_trace_details() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
from st2client.models import Resource, Trace, TriggerInstance, Rule, LiveAction
17
from st2client.exceptions.operations import OperationFailureException
18
from st2client.formatters import table
19
from st2client.formatters import execution as execution_formatter
20
from st2client.commands import resource
21
from st2client.utils.date import format_isodate_for_user_timezone
22
23
24
TRACE_ATTRIBUTE_DISPLAY_ORDER = ['id', 'trace_tag', 'action_executions', 'rules',
25
                                 'trigger_instances', 'start_timestamp']
26
27
TRACE_HEADER_DISPLAY_ORDER = ['id', 'trace_tag', 'start_timestamp']
28
29
TRACE_COMPONENT_DISPLAY_LABELS = ['id', 'type', 'ref', 'updated_at']
30
31
TRACE_DISPLAY_ATTRIBUTES = ['all']
32
33
TRIGGER_INSTANCE_DISPLAY_OPTIONS = [
34
    'all',
35
    'trigger-instances',
36
    'trigger_instances',
37
    'triggerinstances',
38
    'triggers'
39
]
40
41
ACTION_EXECUTION_DISPLAY_OPTIONS = [
42
    'all',
43
    'executions',
44
    'action-executions',
45
    'action_executions',
46
    'actionexecutions',
47
    'actions'
48
]
49
50
51
class TraceBranch(resource.ResourceBranch):
52
    def __init__(self, description, app, subparsers, parent_parser=None):
53
        super(TraceBranch, self).__init__(
54
            Trace, description, app, subparsers,
55
            parent_parser=parent_parser,
56
            read_only=True,
57
            commands={
58
                'list': TraceListCommand,
59
                'get': TraceGetCommand
60
            })
61
62
63
class SingleTraceDisplayMixin(object):
64
65
    def print_trace_details(self, trace, args, **kwargs):
66
        options = {'attributes': TRACE_ATTRIBUTE_DISPLAY_ORDER if args.json else
67
                   TRACE_HEADER_DISPLAY_ORDER}
68
        options['json'] = args.json
69
        options['yaml'] = args.yaml
70
        options['attribute_transform_functions'] = self.attribute_transform_functions
71
72
        formatter = execution_formatter.ExecutionResult
73
74
        self.print_output(trace, formatter, **options)
75
76
        # Everything should be printed if we are printing json.
77
        if args.json or args.yaml:
78
            return
79
80
        components = []
81
        if any(attr in args.attr for attr in TRIGGER_INSTANCE_DISPLAY_OPTIONS):
82
            components.extend([Resource(**{'id': trigger_instance['object_id'],
83
                                           'type': TriggerInstance._alias.lower(),
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _alias was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
84
                                           'ref': trigger_instance['ref'],
85
                                           'updated_at': trigger_instance['updated_at']})
86
                               for trigger_instance in trace.trigger_instances])
87
        if any(attr in args.attr for attr in ['all', 'rules']):
88
            components.extend([Resource(**{'id': rule['object_id'],
89
                                           'type': Rule._alias.lower(),
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _alias was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
90
                                           'ref': rule['ref'],
91
                                           'updated_at': rule['updated_at']})
92
                               for rule in trace.rules])
93
        if any(attr in args.attr for attr in ACTION_EXECUTION_DISPLAY_OPTIONS):
94
            components.extend([Resource(**{'id': execution['object_id'],
95
                                           'type': LiveAction._alias.lower(),
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _alias was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
96
                                           'ref': execution['ref'],
97
                                           'updated_at': execution['updated_at']})
98
                               for execution in trace.action_executions])
99
        if components:
100
            components.sort(key=lambda resource: resource.updated_at)
101
            self.print_output(components, table.MultiColumnTable,
102
                              attributes=TRACE_COMPONENT_DISPLAY_LABELS,
103
                              json=args.json, yaml=args.yaml)
104
105
106
class TraceListCommand(resource.ResourceCommand, SingleTraceDisplayMixin):
107
    display_attributes = ['id', 'trace_tag', 'start_timestamp']
108
109
    attribute_transform_functions = {
110
        'start_timestamp': format_isodate_for_user_timezone
111
    }
112
113
    attribute_display_order = TRACE_ATTRIBUTE_DISPLAY_ORDER
114
115
    def __init__(self, resource, *args, **kwargs):
0 ignored issues
show
Comprehensibility Bug introduced by
resource is re-defining a name which is already available in the outer-scope (previously defined on line 20).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
116
        super(TraceListCommand, self).__init__(
117
            resource, 'list', 'Get the list of the 50 most recent %s.' %
118
            resource.get_plural_display_name().lower(),
119
            *args, **kwargs)
120
121
        self.group = self.parser.add_mutually_exclusive_group()
122
        self.parser.add_argument('-n', '--last', type=int, dest='last',
123
                                 default=50,
124
                                 help=('List N most recent %s.' %
125
                                       resource.get_plural_display_name().lower()))
126
127
        # Filter options
128
        self.group.add_argument('-c', '--trace-tag', help='Trace-tag to filter the list.')
129
        self.group.add_argument('-e', '--execution', help='Execution to filter the list.')
130
        self.group.add_argument('-r', '--rule', help='Rule to filter the list.')
131
        self.group.add_argument('-g', '--trigger-instance',
132
                                help='TriggerInstance to filter the list.')
133
        # Display options
134
        self.parser.add_argument('-a', '--attr', nargs='+',
135
                                 default=self.display_attributes,
136
                                 help=('List of attributes to include in the '
137
                                       'output. "all" will return all '
138
                                       'attributes.'))
139
        self.parser.add_argument('-w', '--width', nargs='+', type=int,
140
                                 default=None,
141
                                 help=('Set the width of columns in output.'))
142
143
    @resource.add_auth_token_to_kwargs_from_cli
144
    def run(self, args, **kwargs):
145
        # Filtering options
146
        if args.trace_tag:
147
            kwargs['trace_tag'] = args.trace_tag
148
        if args.trigger_instance:
149
            kwargs['trigger_instance'] = args.trigger_instance
150
        if args.execution:
151
            kwargs['execution'] = args.execution
152
        if args.rule:
153
            kwargs['rule'] = args.rule
154
155
        return self.manager.query(limit=args.last, **kwargs)
156
157
    def run_and_print(self, args, **kwargs):
158
        instances = self.run(args, **kwargs)
159
        if instances and len(instances) == 1:
160
            # For a single Trace we must include the components unless
161
            # user has overriden the attributes to display
162
            if args.attr == self.display_attributes:
163
                args.attr = ['all']
164
            self.print_trace_details(trace=instances[0], args=args)
165
        else:
166
            self.print_output(reversed(instances), table.MultiColumnTable,
167
                              attributes=args.attr, widths=args.width,
168
                              json=args.json, yaml=args.yaml,
169
                              attribute_transform_functions=self.attribute_transform_functions)
170
171
172
class TraceGetCommand(resource.ResourceGetCommand, SingleTraceDisplayMixin):
173
    display_attributes = ['all']
174
    attribute_display_order = TRACE_ATTRIBUTE_DISPLAY_ORDER
175
    attribute_transform_functions = {
176
        'start_timestamp': format_isodate_for_user_timezone
177
    }
178
179
    pk_argument_name = 'id'
180
181
    def __init__(self, resource, *args, **kwargs):
0 ignored issues
show
Comprehensibility Bug introduced by
resource is re-defining a name which is already available in the outer-scope (previously defined on line 20).

It is generally a bad practice to shadow variables from the outer-scope. In most cases, this is done unintentionally and might lead to unexpected behavior:

param = 5

class Foo:
    def __init__(self, param):   # "param" would be flagged here
        self.param = param
Loading history...
182
        super(TraceGetCommand, self).__init__(resource, *args, **kwargs)
183
184
        # Causation chains
185
        self.causation_group = self.parser.add_mutually_exclusive_group()
186
187
        self.causation_group.add_argument('-e', '--execution',
188
                                          help='Execution to show causation chain.')
189
        self.causation_group.add_argument('-r', '--rule', help='Rule to show causation chain.')
190
        self.causation_group.add_argument('-g', '--trigger-instance',
191
                                          help='TriggerInstance to show causation chain.')
192
193
        # display filter group
194
        self.display_filter_group = self.parser.add_argument_group()
195
196
        self.display_filter_group.add_argument('--show-executions', action='store_true',
197
                                              help='Only show executions.')
198
        self.display_filter_group.add_argument('--show-rules', action='store_true',
199
                                              help='Only show rules.')
200
        self.display_filter_group.add_argument('--show-trigger-instances', action='store_true',
201
                                              help='Only show trigger instances.')
202
        self.display_filter_group.add_argument('-n', '--hide-noop-triggers', action='store_true',
203
                                              help='Hide noop trigger instances.')
204
205
    @resource.add_auth_token_to_kwargs_from_cli
206
    def run(self, args, **kwargs):
207
        resource_id = getattr(args, self.pk_argument_name, None)
208
        return self.get_resource_by_id(resource_id, **kwargs)
209
210
    @resource.add_auth_token_to_kwargs_from_cli
211
    def run_and_print(self, args, **kwargs):
212
        trace = None
213
        try:
214
            trace = self.run(args, **kwargs)
215
        except resource.ResourceNotFoundError:
216
            self.print_not_found(args.id)
217
            raise OperationFailureException('Trace %s not found.' % (args.id))
218
        # First filter for causation chains
219
        trace = self._filter_trace_components(trace=trace, args=args)
220
        # next filter for display purposes
221
        trace = self._apply_display_filters(trace=trace, args=args)
222
        return self.print_trace_details(trace=trace, args=args)
223
224
    @staticmethod
225
    def _filter_trace_components(trace, args):
226
        """
227
        This function walks up the component causal chain. It only returns
228
        properties in the causal chain and nothing else.
229
        """
230
        # check if any filtering is desired
231
        if not (args.execution or args.rule or args.trigger_instance):
232
            return trace
233
234
        component_id = None
235
        component_type = None
236
237
        # pick the right component type
238
        if args.execution:
239
            component_id = args.execution
240
            component_type = 'action_execution'
241
        elif args.rule:
242
            component_id = args.rule
243
            component_type = 'rule'
244
        elif args.trigger_instance:
245
            component_id = args.trigger_instance
246
            component_type = 'trigger_instance'
247
248
        # Initialize collection to use
249
        action_executions = []
250
        rules = []
251
        trigger_instances = []
252
253
        # setup flag to properly manage termination conditions
254
        search_target_found = component_id and component_type
255
256
        while search_target_found:
257
            components_list = []
258
            if component_type == 'action_execution':
259
                components_list = trace.action_executions
260
                to_update_list = action_executions
261
            elif component_type == 'rule':
262
                components_list = trace.rules
263
                to_update_list = rules
264
            elif component_type == 'trigger_instance':
265
                components_list = trace.trigger_instances
266
                to_update_list = trigger_instances
267
            # Look for search_target in the right collection and
268
            # once found look up the caused_by to keep movig up
269
            # the chain.
270
            search_target_found = False
271
            # init to default value
272
            component_caused_by_id = None
273
            for component in components_list:
274
                test_id = component['object_id']
275
                if test_id == component_id:
276
                    caused_by = component.get('caused_by', {})
277
                    component_id = caused_by.get('id', None)
278
                    component_type = caused_by.get('type', None)
279
                    # If provided the component_caused_by_id must match as well. This is mostly
280
                    # applicable for rules since the same rule may appear multiple times and can
281
                    # only be distinguished by causing TriggerInstance.
282
                    if component_caused_by_id and component_caused_by_id != component_id:
283
                        continue
284
                    component_caused_by_id = None
285
                    to_update_list.append(component)
286
                    # In some cases the component_id and the causing component are combined to
287
                    # provide the complete causation chain. Think rule + triggerinstance
288
                    if component_id and ':' in component_id:
289
                        component_id_split = component_id.split(':')
290
                        component_id = component_id_split[0]
291
                        component_caused_by_id = component_id_split[1]
292
                    search_target_found = True
293
                    break
294
295
        trace.action_executions = action_executions
296
        trace.rules = rules
297
        trace.trigger_instances = trigger_instances
298
        return trace
299
300
    @staticmethod
301
    def _apply_display_filters(trace, args):
302
        """
303
        This function looks at the disaply filters to determine which components
304
        should be displayed.
305
        """
306
        # If all the filters are false nothing is to be filtered.
307
        all_component_types = not(args.show_executions or
308
                                  args.show_rules or
309
                                  args.show_trigger_instances)
310
311
        # check if noop_triggers are to be hidden. This check applies whenever TriggerInstances
312
        # are to be shown.
313
        if (all_component_types or args.show_trigger_instances) and args.hide_noop_triggers:
314
            filtered_trigger_instances = []
315
            for trigger_instance in trace.trigger_instances:
316
                is_noop_trigger_instance = True
317
                for rule in trace.rules:
318
                    caused_by_id = rule.get('caused_by', {}).get('id', None)
319
                    if caused_by_id == trigger_instance['object_id']:
320
                        is_noop_trigger_instance = False
321
                if not is_noop_trigger_instance:
322
                    filtered_trigger_instances.append(trigger_instance)
323
            trace.trigger_instances = filtered_trigger_instances
324
325
        if all_component_types:
326
            return trace
327
328
        if not args.show_executions:
329
            trace.action_executions = []
330
331
        if not args.show_rules:
332
            trace.rules = []
333
334
        if not args.show_trigger_instances:
335
            trace.trigger_instances = []
336
337
        return trace
338