-
Notifications
You must be signed in to change notification settings - Fork 4
/
nsb_config.py
245 lines (176 loc) · 7.43 KB
/
nsb_config.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
import argparse
import configparser
import os
import sys
from pprint import pprint
########################## CONFIG FILE SETTINGS ###########################
# this isn't used directly by the module,
# it just has to be located *somewhere*
default_global_path = 'necro_score_bot.conf'
# parameters to expect from global config file
# additional entries in file are ignored
_expect_common_config = [('dry-run', bool),
('data', str),
('debug', bool),
('steam_key', str),
('twitter_keys', str),
]
_expect_global_config = _expect_common_config + [('config', str)]
# allow missing entries/sections/file in global config file?
_tolerate_missing_global_entries = False
_tolerate_missing_global_file = False
_expect_user_config = _expect_common_config
_tolerate_missing_user_entries = True
####################### COMMAND-LINE ARGS SETTINGS ########################
def _bool(string):
if string.lower() == 'true':
return True
elif string.lower() == 'false':
return False
msg = "%r is not a boolean" % string
raise argparse.ArgumentTypeError(msg)
def _dir(string):
string = os.path.expanduser(string)
if not os.path.isdir(string):
print('Creating directory', string)
os.mkdir(string)
return string
_parser = argparse.ArgumentParser()
# commands
_parser.add_argument('action',
help='action to perform',
choices=['init', 'postDaily', 'update', 'printBoard', 'none'])
# flags
_parser.add_argument('--config', help='specify config path',
metavar='DIRECTORY')
_parser.add_argument('--data', help='specify data path',
metavar='DIRECTORY', type=_dir)
_parser.add_argument('--steam-key', help='specify file with steam keys',
metavar='FILE')
_parser.add_argument('--twitter-keys', help='Specify directory with twitter keys. Set to None to disable twitter.',
metavar='DIRECTORY')
# --dry-run requires dest, otherwise it
# stores it as dry_run instead of dry-run
_parser.add_argument('--dry-run', help="Don't tweet, download or change any files",
action='store_true', default=False, dest='dry-run')
_parser.add_argument('--handle-new', help="Handle boards without history",
action='store_true', default=False, dest='handle-new')
_parser.add_argument('--debug', help='display debug messages',
action='store_true', default=False)
_parser.add_argument('--tweet', help='enable tweeting',
action='store_true', default=False)
_parser.add_argument('--backup', help='backup files to history after downloading',
metavar='bool', type=_bool)
_parser.add_argument('--churn', help='churn through changes quickly, not composing or posting any messages',
action='store_true', default=False)
###########################################################################
def get_command_line_args():
args = _parser.parse_args().__dict__
return {k: v for k, v in args.items()
if v is not None}
def get_global_options(path):
path = evaluate_path(path)
return _get_config_args(path, _expect_global_config,
_tolerate_missing_global_file,
_tolerate_missing_global_entries)
def get_user_options(path, tolerate_missing_file):
path = evaluate_path(path)
return _get_config_args(path, _expect_user_config,
tolerate_missing_file,
_tolerate_missing_user_entries)
def merge_options(*args):
"""
Merges option dicts.
Later arguments have priority over earlier ones.
Example: merge_options(global_conf, user_conf, cmnd_ln_args)
"""
result = {}
for new_values in args:
for key, value in new_values.items():
if value is None:
continue
result[key] = value
return result
def evaluate_path(path, add_trailing_slash=False):
"""
Evaluates user/environment variables and relative path.
"""
path = os.path.expanduser(path)
path = os.path.expandvars(path)
path = os.path.abspath(path)
if add_trailing_slash:
path += '/'
return path
def _get_config_args(path, expected_entries, tolerate_missing_file, tolerate_missing_entries):
result = {}
parser = configparser.ConfigParser()
parser.read(path)
# more friendly error message in case file/section is missing
if 'general' not in parser:
message = 'section (or whole file) [general] missing from {}' \
''.format(path)
if tolerate_missing_file:
print('NOTICE: ' + message)
return {}
else:
raise Exception('ERROR: ' + message)
# look up expected entries and convert as specified
for entry, conversion_type in expected_entries:
if conversion_type is bool:
value = parser['general'].getboolean(entry)
else:
value = parser['general'].get(entry)
if value is None:
message = 'could not find entry "{}" in section [general] in ' \
'config file {}'.format(entry, path)
if not tolerate_missing_entries:
raise Exception('ERROR: ' + message)
else:
result[entry] = conversion_type(value)
return result
def readFile(path):
f = open(path)
result = f.read()
f.close()
return result.rstrip()
def createDir(path, options):
if not os.path.isdir(path):
print(path, "doesn't exist, creating")
if not options['dry-run']:
os.mkdir(path)
def evaluate_paths(options):
options['twitter_keys'] = evaluate_path(options['twitter_keys'], True)
options['data'] = evaluate_path(options['data'], True)
options['config'] = evaluate_path(options['config'], False)
options['steam_key'] = readFile(evaluate_path(options['steam_key'], False))
return options
def create_paths(options):
createDir(options['data'], options)
createDir(options['data'] + 'curr/', options) #TODO: needed?
createDir(options['data'] + 'last/', options) #TODO: needed?
createDir(options['data'] + 'boards/', options)
def read_options():
#Will exit here if --help is supplied
cl_options = get_command_line_args()
debug = cl_options['debug']
if debug:
print("cl_options: ", end='')
pprint(sorted(cl_options.items()))
global_path = default_global_path
global_options = get_global_options(global_path)
user_path = cl_options.get('config') or global_options['config']
user_path = evaluate_path(user_path)
#We tolerate missing file if 'config' is not supplied
#at the command line
user_options = get_user_options(user_path,
'config' not in cl_options)
options = global_options.copy()
options.update(user_options)
options.update(cl_options)
options = evaluate_paths(options)
create_paths(options)
if debug:
print("all_options: ", end='')
pprint(sorted(options.items()))
return options
options = read_options()