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.
Passed
Push — develop-v1.3.1 ( 969842...8fa207 )
by
unknown
05:56
created

PropertyValueTable.format()   F

Complexity

Conditions 14

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 14
dl 0
loc 44
rs 2.7581

How to fix   Complexity   

Complexity

Complex classes like PropertyValueTable.format() 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
import json
17
import math
18
import logging
19
20
from prettytable import PrettyTable
21
from six.moves import zip
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in zip.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
22
23
from st2client import formatters
24
from st2client.utils import strutil
25
from st2client.utils.terminal import get_terminal_size
26
27
28
LOG = logging.getLogger(__name__)
29
30
# Minimum width for the ID to make sure the ID column doesn't wrap across
31
# multiple lines
32
MIN_ID_COL_WIDTH = 26
33
34
# Minimum width for a column
35
MIN_COL_WIDTH = 5
36
37
# Default attribute display order to use if one is not provided
38
DEFAULT_ATTRIBUTE_DISPLAY_ORDER = ['id', 'name', 'pack', 'description']
39
40
# Attributes which contain bash escape sequences - we can't split those across multiple lines
41
# since things would break
42
COLORIZED_ATTRIBUTES = {
43
    'status': {
44
        'col_width': 24  # Note: len('succeed' + ' (XXXX elapsed)') <= 24
45
    }
46
}
47
48
49
class MultiColumnTable(formatters.Formatter):
50
51
    @classmethod
52
    def format(cls, entries, *args, **kwargs):
53
        attributes = kwargs.get('attributes', [])
54
        attribute_transform_functions = kwargs.get('attribute_transform_functions', {})
55
        widths = kwargs.get('widths', [])
56
        widths = widths or []
57
58
        if not widths and attributes:
59
            # Dynamically calculate column size based on the terminal size
60
            lines, cols = get_terminal_size()
61
62
            if attributes[0] == 'id':
63
                # consume iterator and save as entries so collection is accessible later.
64
                entries = [e for e in entries]
65
                # first column contains id, make sure it's not broken up
66
                first_col_width = cls._get_required_column_width(values=[e.id for e in entries],
67
                                                                 minimum_width=MIN_ID_COL_WIDTH)
68
                cols = (cols - first_col_width)
69
                col_width = int(math.floor((cols / len(attributes))))
70
            else:
71
                col_width = int(math.floor((cols / len(attributes))))
72
                first_col_width = col_width
73
74
            widths = []
75
            subtract = 0
76
            for index in range(0, len(attributes)):
77
                attribute_name = attributes[index]
78
79
                if index == 0:
80
                    widths.append(first_col_width)
81
                    continue
82
83
                if attribute_name in COLORIZED_ATTRIBUTES:
84
                    current_col_width = COLORIZED_ATTRIBUTES[attribute_name]['col_width']
85
                    subtract += (current_col_width - col_width)
86
                else:
87
                    # Make sure we subtract the added width from the last column so we account
88
                    # for the fixed width columns and make sure table is not wider than the
89
                    # terminal width.
90
                    if index == (len(attributes) - 1) and subtract:
91
                        current_col_width = (col_width - subtract)
92
93
                        if current_col_width <= MIN_COL_WIDTH:
94
                            # Make sure column width is always grater than MIN_COL_WIDTH
95
                            current_col_width = MIN_COL_WIDTH
96
                    else:
97
                        current_col_width = col_width
98
99
                widths.append(current_col_width)
100
101
        if not attributes or 'all' in attributes:
102
            attributes = sorted([attr for attr in entries[0].__dict__
103
                                 if not attr.startswith('_')])
104
105
        # Determine table format.
106
        if len(attributes) == len(widths):
107
            # Customize width for each column.
108
            columns = zip(attributes, widths)
109
        else:
110
            # If only 1 width value is provided then
111
            # apply it to all columns else fix at 28.
112
            width = widths[0] if len(widths) == 1 else 28
113
            columns = zip(attributes,
114
                          [width for i in range(0, len(attributes))])
115
116
        # Format result to table.
117
        table = PrettyTable()
118
        for column in columns:
119
            table.field_names.append(column[0])
120
            table.max_width[column[0]] = column[1]
121
        table.padding_width = 1
122
        table.align = 'l'
123
        table.valign = 't'
124
        for entry in entries:
125
            # TODO: Improve getting values of nested dict.
126
            values = []
127
            for field_name in table.field_names:
128
                if '.' in field_name:
129
                    field_names = field_name.split('.')
130
                    value = getattr(entry, field_names.pop(0), {})
131
                    for name in field_names:
132
                        value = cls._get_field_value(value, name)
133
                        if type(value) is str:
134
                            break
135
                    value = strutil.unescape(value)
136
                    values.append(value)
137
                else:
138
                    value = cls._get_simple_field_value(entry, field_name)
139
                    transform_function = attribute_transform_functions.get(field_name,
140
                                                                          lambda value: value)
141
                    value = transform_function(value=value)
142
                    value = strutil.unescape(value)
143
                    values.append(value)
144
            table.add_row(values)
145
        return table
146
147
    @staticmethod
148
    def _get_simple_field_value(entry, field_name):
149
        """
150
        Format a value for a simple field.
151
        """
152
        value = getattr(entry, field_name, '')
153
        if isinstance(value, (list, tuple)):
154
            if len(value) == 0:
155
                value = ''
156
            elif isinstance(value[0], (str, unicode)):
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'unicode'
Loading history...
157
                # List contains simple string values, format it as comma
158
                # separated string
159
                value = ', '.join(value)
160
161
        return value
162
163
    @staticmethod
164
    def _get_field_value(value, field_name):
165
        r_val = value.get(field_name, None)
166
        if r_val is None:
167
            return ''
168
169
        if isinstance(r_val, list) or isinstance(r_val, dict):
170
            return r_val if len(r_val) > 0 else ''
171
        return r_val
172
173
    @staticmethod
174
    def _get_friendly_column_name(name):
175
        if not name:
176
            return None
177
178
        friendly_name = name.replace('_', ' ').replace('.', ' ').capitalize()
179
        return friendly_name
180
181
    @staticmethod
182
    def _get_required_column_width(values, minimum_width=0):
183
        max_width = len(max(values, key=len)) if values else minimum_width
184
        return max_width if max_width > minimum_width else minimum_width
185
186
187
class PropertyValueTable(formatters.Formatter):
188
189
    @classmethod
190
    def format(cls, subject, *args, **kwargs):
191
        attributes = kwargs.get('attributes', None)
192
        attribute_display_order = kwargs.get('attribute_display_order',
193
                                             DEFAULT_ATTRIBUTE_DISPLAY_ORDER)
194
        attribute_transform_functions = kwargs.get('attribute_transform_functions', {})
195
196
        if not attributes or 'all' in attributes:
197
            attributes = sorted([attr for attr in subject.__dict__
198
                                 if not attr.startswith('_')])
199
200
        for attr in attribute_display_order[::-1]:
201
            if attr in attributes:
202
                attributes.remove(attr)
203
                attributes = [attr] + attributes
204
        table = PrettyTable()
205
        table.field_names = ['Property', 'Value']
206
        table.max_width['Property'] = 20
207
        table.max_width['Value'] = 60
208
        table.padding_width = 1
209
        table.align = 'l'
210
        table.valign = 't'
211
212
        for attribute in attributes:
213
            if '.' in attribute:
214
                field_names = attribute.split('.')
215
                value = cls._get_attribute_value(subject, field_names.pop(0))
216
                for name in field_names:
217
                    value = cls._get_attribute_value(value, name)
218
                    if type(value) is str:
219
                        break
220
            else:
221
                value = cls._get_attribute_value(subject, attribute)
222
223
            transform_function = attribute_transform_functions.get(attribute,
224
                                                                   lambda value: value)
225
            value = transform_function(value=value)
226
227
            if type(value) is dict or type(value) is list:
228
                value = json.dumps(value, indent=4)
229
230
            value = strutil.unescape(value)
231
            table.add_row([attribute, value])
232
        return table
233
234
    @staticmethod
235
    def _get_attribute_value(subject, attribute):
236
        if isinstance(subject, dict):
237
            r_val = subject.get(attribute, None)
238
        else:
239
            r_val = getattr(subject, attribute, None)
240
        if r_val is None:
241
            return ''
242
        if isinstance(r_val, list) or isinstance(r_val, dict):
243
            return r_val if len(r_val) > 0 else ''
244
        return r_val
245