1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
# ----------------------------------------------------------------------------- |
3
|
|
|
# Copyright (c) 2016 Continuum Analytics, Inc. |
4
|
|
|
# |
5
|
|
|
# Licensed under the terms of the MIT License |
6
|
|
|
# (see LICENSE.txt for details) |
7
|
|
|
# ----------------------------------------------------------------------------- |
8
|
|
|
"""Configuration module and parameters.""" |
9
|
|
|
|
10
|
|
|
# Standard library imports |
11
|
|
|
import os |
12
|
|
|
|
13
|
|
|
# Third party imports |
14
|
|
|
from six.moves import configparser |
15
|
|
|
|
16
|
|
|
# Version control modes |
17
|
|
|
STAGED_MODE = 'staged' |
18
|
|
|
UNSTAGED_MODE = 'unstaged' |
19
|
|
|
COMMITED_MODE = 'commited' |
20
|
|
|
|
21
|
|
|
# File modes |
22
|
|
|
MODIFIED_LINES = 'lines' |
23
|
|
|
MODIFIED_FILES = 'files' |
24
|
|
|
ALL_FILES = 'all' |
25
|
|
|
|
26
|
|
|
# Configuration constants |
27
|
|
|
DEFAULT_BRANCH = 'origin/master' |
28
|
|
|
DEFAULT_IGNORE_EXTENSIONS = ('orig', 'pyc') |
29
|
|
|
DEFAULT_IGNORE_FOLDERS = ('build', '__pychache__') |
30
|
|
|
MAIN_CONFIG_SECTION = 'ciocheck' |
31
|
|
|
CONFIGURATION_FILE = '.ciocheck' |
32
|
|
|
COVERAGE_CONFIGURATION_FILE = '.coveragerc' |
33
|
|
|
|
34
|
|
|
COPYRIGHT_HEADER_FILE = '.ciocopyright' |
35
|
|
|
|
36
|
|
|
DEFAULT_ENCODING_HEADER = u"# -*- coding: utf-8 -*-\n" |
37
|
|
|
DEFAULT_COPYRIGHT_HEADER = u""" |
38
|
|
|
# ----------------------------------------------------------------------------- |
39
|
|
|
# Copyright (c) 2016 Continuum Analytics, Inc. |
40
|
|
|
# |
41
|
|
|
# May be copied and distributed freely only as part of an Anaconda or |
42
|
|
|
# Miniconda installation. |
43
|
|
|
# ----------------------------------------------------------------------------- |
44
|
|
|
""".lstrip() |
45
|
|
|
|
46
|
|
|
DEFAULT_CIOCHECK_CONFIG = { |
47
|
|
|
# Global options |
48
|
|
|
'branch': DEFAULT_BRANCH, |
49
|
|
|
'diff_mode': STAGED_MODE, |
50
|
|
|
'file_mode': MODIFIED_LINES, |
51
|
|
|
# Python specific/ pyformat |
52
|
|
|
'header': DEFAULT_ENCODING_HEADER, |
53
|
|
|
'copyright_file': COPYRIGHT_HEADER_FILE, |
54
|
|
|
'add_copyright': True, |
55
|
|
|
'add_header': True, |
56
|
|
|
'add_init': True, |
57
|
|
|
# Linters/Formaters/Testers |
58
|
|
|
'check': ['pep8'], |
59
|
|
|
'enforce': [], |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
class CustomConfigParser(configparser.ConfigParser): |
64
|
|
|
""" |
65
|
|
|
Custom config parser that turns options into python objects. |
66
|
|
|
|
67
|
|
|
Support for bool, and lists only. |
68
|
|
|
""" |
69
|
|
|
|
70
|
|
|
SECTION = MAIN_CONFIG_SECTION |
71
|
|
|
|
72
|
|
|
def get_value(self, option, section=MAIN_CONFIG_SECTION): |
73
|
|
|
"""Get config value from the defailt main section.""" |
74
|
|
|
default_value = DEFAULT_CIOCHECK_CONFIG.get(option) |
75
|
|
|
val = self.get(self.SECTION, option) |
76
|
|
|
if isinstance(default_value, bool): |
77
|
|
|
value = True if val.lower() == 'true' else False |
78
|
|
|
elif isinstance(default_value, list): |
79
|
|
|
if val: |
80
|
|
|
value = val.split(',') |
81
|
|
|
else: |
82
|
|
|
value = '' |
83
|
|
|
value = [v.strip() for v in value] |
84
|
|
|
else: |
85
|
|
|
value = val |
86
|
|
|
return value |
87
|
|
|
|
88
|
|
|
def set_value(self, option, value, section=MAIN_CONFIG_SECTION): |
89
|
|
|
"""Set config value on the defailt main section.""" |
90
|
|
|
default_value = DEFAULT_CIOCHECK_CONFIG.get(option) |
91
|
|
|
if not self.has_section(self.SECTION): |
92
|
|
|
self.add_section(self.SECTION) |
93
|
|
|
|
94
|
|
|
if isinstance(default_value, bool): |
95
|
|
|
val = 'true' if value else 'false' |
96
|
|
|
self.set(self.SECTION, option, val) |
97
|
|
|
elif isinstance(default_value, list): |
98
|
|
|
if default_value: |
99
|
|
|
val = ','.join(value) |
100
|
|
|
else: |
101
|
|
|
val = '' |
102
|
|
|
self.set(self.SECTION, option, val) |
103
|
|
|
else: |
104
|
|
|
self.set(self.SECTION, option, value) |
105
|
|
|
|
106
|
|
|
|
107
|
|
|
def load_file_config(folder, file_name=None): |
108
|
|
|
""" |
109
|
|
|
Load configuration at `folder` or `file_name` and return the parser. |
110
|
|
|
|
111
|
|
|
file_name is assumed to be located on folder. |
112
|
|
|
""" |
113
|
|
|
if file_name is None: |
114
|
|
|
config_path = os.path.join(folder, CONFIGURATION_FILE) |
115
|
|
|
else: |
116
|
|
|
config_path = os.path.join(folder, file_name) |
117
|
|
|
|
118
|
|
|
config = CustomConfigParser() |
119
|
|
|
if os.path.isfile(config_path): |
120
|
|
|
with open(config_path, 'r') as file_obj: |
121
|
|
|
config.readfp(file_obj) |
122
|
|
|
|
123
|
|
|
if config.has_option(MAIN_CONFIG_SECTION, 'inherit_config'): |
124
|
|
|
base_config_file = config[MAIN_CONFIG_SECTION]['inherit_config'] |
125
|
|
|
base_config_path = os.path.join(folder, base_config_file) |
126
|
|
|
|
127
|
|
|
# If a config file refers to itself, avoid entering and endless |
128
|
|
|
# recursion |
129
|
|
|
if config_path != base_config_path: |
130
|
|
|
base_config = load_file_config( |
131
|
|
|
folder=folder, file_name=base_config_file) |
132
|
|
|
|
133
|
|
|
# Merge the config files |
134
|
|
|
for section in config: |
135
|
|
|
for opt in config[section]: |
136
|
|
|
base_config[section][opt] = config[section][opt] |
137
|
|
|
|
138
|
|
|
config = base_config |
139
|
|
|
|
140
|
|
|
return config |
141
|
|
|
|
142
|
|
|
|
143
|
|
|
def load_config(folder, cli_args): |
144
|
|
|
"""Load the configuration, load defaults and return the parser.""" |
145
|
|
|
config = load_file_config(folder, file_name=cli_args.config_file) |
146
|
|
|
|
147
|
|
|
for key, value in DEFAULT_CIOCHECK_CONFIG.items(): |
148
|
|
|
if not config.has_option(MAIN_CONFIG_SECTION, key): |
149
|
|
|
config.set_value(key, value) |
150
|
|
|
|
151
|
|
|
if hasattr(cli_args, key): |
152
|
|
|
cli_value = getattr(cli_args, key) |
153
|
|
|
if cli_value: |
154
|
|
|
config.set_value(key, cli_value) |
155
|
|
|
|
156
|
|
|
return config |
157
|
|
|
|