Completed
Push — master ( bb5487...4fc1d0 )
by Manas
13:01 queued 05:18
created

st2client.CLIConfigParser.parse()   F

Complexity

Conditions 10

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 40
rs 3.1304
cc 10

How to fix   Complexity   

Complexity

Complex classes like st2client.CLIConfigParser.parse() 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
"""
17
Module for parsing CLI config file.
18
"""
19
20
import os
21
22
from collections import defaultdict
23
24
import six
25
from six.moves.configparser import ConfigParser
26
27
__all__ = [
28
    'CLIConfigParser',
29
30
    'ST2_CONFIG_DIRECTORY',
31
    'ST2_CONFIG_PATH',
32
33
    'CONFIG_DEFAULT_VALUES'
34
]
35
36
ST2_CONFIG_DIRECTORY = '~/.st2'
37
ST2_CONFIG_DIRECTORY = os.path.abspath(os.path.expanduser(ST2_CONFIG_DIRECTORY))
38
39
ST2_CONFIG_PATH = os.path.abspath(os.path.join(ST2_CONFIG_DIRECTORY, 'config'))
40
41
CONFIG_FILE_OPTIONS = {
42
    'general': {
43
        'base_url': {
44
            'type': 'string',
45
            'default': None
46
        },
47
        'api_version': {
48
            'type': 'string',
49
            'default': None
50
        },
51
        'cacert': {
52
            'type': 'string',
53
            'default': None
54
        },
55
        'silence_ssl_warnings': {
56
            'type': 'bool',
57
            'default': False
58
        }
59
    },
60
    'cli': {
61
        'debug': {
62
            'type': 'bool',
63
            'default': False
64
        },
65
        'cache_token': {
66
            'type': 'boolean',
67
            'default': True
68
        }
69
    },
70
    'credentials': {
71
        'username': {
72
            'type': 'string',
73
            'default': None
74
        },
75
        'password': {
76
            'type': 'string',
77
            'default': None
78
        }
79
    },
80
    'api': {
81
        'url': {
82
            'type': 'string',
83
            'default': None
84
        }
85
    },
86
    'auth': {
87
        'url': {
88
            'type': 'string',
89
            'default': None
90
        }
91
    }
92
}
93
94
CONFIG_DEFAULT_VALUES = {}
95
96
for section, keys in six.iteritems(CONFIG_FILE_OPTIONS):
97
    CONFIG_DEFAULT_VALUES[section] = {}
98
99
    for key, options in six.iteritems(keys):
100
        default_value = options['default']
101
        CONFIG_DEFAULT_VALUES[section][key] = default_value
102
103
104
class CLIConfigParser(object):
105
    def __init__(self, config_file_path, validate_config_exists=True):
106
        if validate_config_exists and not os.path.isfile(config_file_path):
107
            raise ValueError('Config file "%s" doesn\'t exist')
108
109
        self.config_file_path = config_file_path
110
111
    def parse(self):
112
        """
113
        Parse the config and return a dict with the parsed values.
114
115
        :rtype: ``dict``
116
        """
117
        result = defaultdict(dict)
118
119
        if not os.path.isfile(self.config_file_path):
120
            # Config doesn't exist, return the default values
121
            return CONFIG_DEFAULT_VALUES
122
123
        config = ConfigParser()
124
        with open(self.config_file_path, 'r') as fp:
125
            config.readfp(fp)
126
127
        for section, keys in six.iteritems(CONFIG_FILE_OPTIONS):
0 ignored issues
show
Comprehensibility Bug introduced by
section is re-defining a name which is already available in the outer-scope (previously defined on line 96).

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...
Comprehensibility Bug introduced by
keys is re-defining a name which is already available in the outer-scope (previously defined on line 96).

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...
128
            for key, options in six.iteritems(keys):
0 ignored issues
show
Comprehensibility Bug introduced by
key is re-defining a name which is already available in the outer-scope (previously defined on line 99).

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...
Comprehensibility Bug introduced by
options is re-defining a name which is already available in the outer-scope (previously defined on line 99).

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...
129
                key_type = options['type']
130
                key_default_value = options['default']
131
132
                if config.has_option(section, key):
133
                    if key_type in ['str', 'string']:
134
                        get_func = config.get
135
                    elif key_type in ['int', 'integer']:
136
                        get_func = config.getint
137
                    elif key_type in ['float']:
138
                        get_func = config.getfloat
139
                    elif key_type in ['bool', 'boolean']:
140
                        get_func = config.getboolean
141
                    else:
142
                        msg = 'Invalid type "%s" for option "%s"' % (key_type, key)
143
                        raise ValueError(msg)
144
145
                    value = get_func(section, key)
146
                    result[section][key] = value
147
                else:
148
                    result[section][key] = key_default_value
149
150
        return dict(result)
151