/
config.py
285 lines (224 loc) · 7.68 KB
/
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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
"""Package used to manage the .mackup.cfg config file."""
import os
import os.path
from .constants import (
CUSTOM_APPS_DIR,
MACKUP_BACKUP_PATH,
MACKUP_CONFIG_FILE,
ENGINE_DROPBOX,
ENGINE_GDRIVE,
ENGINE_COPY,
ENGINE_ICLOUD,
ENGINE_FS,
)
from .utils import (
error,
get_dropbox_folder_location,
get_copy_folder_location,
get_google_drive_folder_location,
get_icloud_folder_location,
)
try:
import configparser
except ImportError:
import ConfigParser as configparser
class Config(object):
"""The Mackup Config class."""
def __init__(self, filename=None):
"""
Create a Config instance.
Args:
filename (str): Optional filename of the config file. If empty,
defaults to MACKUP_CONFIG_FILE
"""
assert isinstance(filename, str) or filename is None
# Initialize the parser
self._parser = self._setup_parser(filename)
# Do we have an old config file ?
self._warn_on_old_config()
# Get the storage engine
self._engine = self._parse_engine()
# Get the path where the Mackup folder is
self._path = self._parse_path()
# Get the directory replacing 'Mackup', if any
self._directory = self._parse_directory()
# Get the list of apps to ignore
self._apps_to_ignore = self._parse_apps_to_ignore()
# Get the list of apps to allow
self._apps_to_sync = self._parse_apps_to_sync()
@property
def engine(self):
"""
The engine used by the storage.
ENGINE_DROPBOX, ENGINE_GDRIVE, ENGINE_COPY, ENGINE_ICLOUD or ENGINE_FS.
Returns:
str
"""
return str(self._engine)
@property
def path(self):
"""
Path to the Mackup configuration files.
The path to the directory where Mackup is gonna create and store his
directory.
Returns:
str
"""
return str(self._path)
@property
def directory(self):
"""
The name of the Mackup directory, named Mackup by default.
Returns:
str
"""
return str(self._directory)
@property
def fullpath(self):
"""
Full path to the Mackup configuration files.
The full path to the directory when Mackup is storing the configuration
files.
Returns:
str
"""
return str(os.path.join(self.path, self.directory))
@property
def apps_to_ignore(self):
"""
Get the list of applications ignored in the config file.
Returns:
set. Set of application names to ignore, lowercase
"""
return set(self._apps_to_ignore)
@property
def apps_to_sync(self):
"""
Get the list of applications allowed in the config file.
Returns:
set. Set of application names to allow, lowercase
"""
return set(self._apps_to_sync)
def _setup_parser(self, filename=None):
"""
Configure the ConfigParser instance the way we want it.
Args:
filename (str) or None
Returns:
SafeConfigParser
"""
assert isinstance(filename, str) or filename is None
# If we are not overriding the config filename
if not filename:
filename = MACKUP_CONFIG_FILE
parser = configparser.SafeConfigParser(allow_no_value=True)
parser.read(os.path.join(os.path.join(os.environ["HOME"], filename)))
return parser
def _warn_on_old_config(self):
"""Warn the user if an old config format is detected."""
# Is an old setion is in the config file ?
old_sections = ["Allowed Applications", "Ignored Applications"]
for old_section in old_sections:
if self._parser.has_section(old_section):
error(
"Old config file detected. Aborting.\n"
"\n"
"An old section (e.g. [Allowed Applications]"
" or [Ignored Applications] has been detected"
" in your {} file.\n"
"I'd rather do nothing than do something you"
" do not want me to do.\n"
"\n"
"Please read the up to date documentation on"
" <https://github.com/lra/mackup> and migrate"
" your configuration file.".format(MACKUP_CONFIG_FILE)
)
def _parse_engine(self):
"""
Parse the storage engine in the config.
Returns:
str
"""
if self._parser.has_option("storage", "engine"):
engine = str(self._parser.get("storage", "engine"))
else:
engine = ENGINE_DROPBOX
assert isinstance(engine, str)
if engine not in [
ENGINE_DROPBOX,
ENGINE_GDRIVE,
ENGINE_COPY,
ENGINE_ICLOUD,
ENGINE_FS,
]:
raise ConfigError("Unknown storage engine: {}".format(engine))
return str(engine)
def _parse_path(self):
"""
Parse the storage path in the config.
Returns:
str
"""
if self.engine == ENGINE_DROPBOX:
path = get_dropbox_folder_location()
elif self.engine == ENGINE_GDRIVE:
path = get_google_drive_folder_location()
elif self.engine == ENGINE_COPY:
path = get_copy_folder_location()
elif self.engine == ENGINE_ICLOUD:
path = get_icloud_folder_location()
elif self.engine == ENGINE_FS:
if self._parser.has_option("storage", "path"):
cfg_path = self._parser.get("storage", "path")
path = os.path.join(os.environ["HOME"], cfg_path)
else:
raise ConfigError(
"The required 'path' can't be found while"
" the 'file_system' engine is used."
)
return str(path)
def _parse_directory(self):
"""
Parse the storage directory in the config.
Returns:
str
"""
if self._parser.has_option("storage", "directory"):
directory = self._parser.get("storage", "directory")
# Don't allow CUSTOM_APPS_DIR as a storage directory
if directory == CUSTOM_APPS_DIR:
raise ConfigError(
"{} cannot be used as a storage directory.".format(CUSTOM_APPS_DIR)
)
else:
directory = MACKUP_BACKUP_PATH
return str(directory)
def _parse_apps_to_ignore(self):
"""
Parse the applications to ignore in the config.
Returns:
set
"""
# We ignore nothing by default
apps_to_ignore = set()
# Is the "[applications_to_ignore]" in the cfg file ?
section_title = "applications_to_ignore"
if self._parser.has_section(section_title):
apps_to_ignore = set(self._parser.options(section_title))
return apps_to_ignore
def _parse_apps_to_sync(self):
"""
Parse the applications to backup in the config.
Returns:
set
"""
# We allow nothing by default
apps_to_sync = set()
# Is the "[applications_to_sync]" section in the cfg file ?
section_title = "applications_to_sync"
if self._parser.has_section(section_title):
apps_to_sync = set(self._parser.options(section_title))
return apps_to_sync
class ConfigError(Exception):
"""Exception used for handle errors in the configuration."""
pass