bbarchivist.loadergen   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 443
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 165
dl 0
loc 443
rs 8.8
c 0
b 0
f 0
ccs 164
cts 164
cp 1
wmc 45

18 Functions

Rating   Name   Duplication   Size   Complexity  
A generate_lazy_set() 0 32 3
A versionpad() 0 10 1
A format_suffix() 0 17 3
A generate_individual_loaders() 0 34 4
A wrap_pseudocap() 0 24 3
A pretty_formatter() 0 18 1
A generate_device() 0 13 3
A read_radio_files() 0 22 1
A read_os_files() 0 26 4
A generate_os_fixes() 0 22 2
A find_signed_file() 0 23 3
A zeropad() 0 16 2
A generate_skeletons() 0 11 3
A generate_lazy_filename() 0 19 3
A generate_filename() 0 20 3
A generate_lazy_loader() 0 32 3
A generate_loaders() 0 32 1
A read_files() 0 20 2

How to fix   Complexity   

Complexity

Complex classes like bbarchivist.loadergen 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
#!/usr/bin/env python3
2 5
"""This module is used for creation of autoloaders."""
3
4 5
import glob  # filename matching
5 5
import os  # path work
6
7 5
from bbarchivist import exceptions  # exception handling
8 5
from bbarchivist import jsonutils  # json
9 5
from bbarchivist import pseudocap  # implement cap
10 5
from bbarchivist import utilities  # directory handler
11
12 5
__author__ = "Thurask"
13 5
__license__ = "WTFPL v2"
14 5
__copyright__ = "2015-2019 Thurask"
15
16
17 5
def read_files(localdir, core=False):
18
    """
19
    Read list of signed files, return name assignments.
20
21
    :param localdir: Directory to use.
22
    :type localdir: str
23
24
    :param core: If we're using a core OS image. Default is false.
25
    :type core: bool
26
    """
27 5
    oslist = read_os_files(localdir, core)
28
    # [8960, 8x30, 8974, ti]
29 5
    radlist = read_radio_files(localdir)
30
    # [ti, z10, z10_vzw, q10, z30, z3, 8974]
31 5
    pairdict = {}
32 5
    mapping = {0:3, 1:0, 2:0, 3:0, 4:0, 5:1, 6:2}
33 5
    for idx, rad in enumerate(radlist):
34 5
        pairdict[rad] = oslist[mapping[idx]]
35 5
    filtdict = {k: v for k, v in pairdict.items() if k}  # pop None
36 5
    return filtdict
37
38
39 5
def find_signed_file(match, localdir, title, silent=False):
40
    """
41
    Use pattern matching to find a signed file in a directory.
42
43
    :param match: Match pattern to use.
44
    :type match: str
45
46
    :param localdir: Directory to use.
47
    :type localdir: str
48
49
    :param title: File type, in case it isn't found.
50
    :type title: str
51
52
    :param silent: Don't print that a file wasn't found. Default is False.
53
    :type silent: bool
54
    """
55 5
    try:
56 5
        signedfile = glob.glob(os.path.join(localdir, match))[0]
57 5
    except IndexError:
58 5
        signedfile = None
59 5
        if not silent:
60 5
            print("No {0} found".format(title))
61 5
    return signedfile
62
63
64 5
def generate_os_fixes(core=False):
65
    """
66
    Generate name regexes for OS signed files.
67
68
    :param core: If we're using a core OS image. Default is false.
69
    :type core: bool
70
    """
71 5
    if core:
72 5
        fix8960 = "*qc8960.*_sfi.BB*.signed"
73 5
        fixomap_new = "*winchester.*_sfi.BB*.signed"
74 5
        fixomap_old = "*os.factory_sfi.BB*.signed"
75 5
        fix8930 = "*qc8x30.BB*.signed"
76 5
        fix8974_new = "*qc8974.BB*.signed"
77 5
        fix8974_old = "*qc8974.*_sfi.BB*.signed"
78
    else:
79 5
        fix8960 = "*qc8960.*_sfi.desktop.BB*.signed"
80 5
        fixomap_new = "*winchester.*_sfi.desktop.BB*.signed"
81 5
        fixomap_old = "*os.factory_sfi.desktop.BB*.signed"
82 5
        fix8930 = "*qc8x30.desktop.BB*.signed"
83 5
        fix8974_new = "*qc8974.desktop.BB*.signed"
84 5
        fix8974_old = "*qc8974.*_sfi.desktop.BB*.signed"
85 5
    return fix8960, fixomap_new, fixomap_old, fix8930, fix8974_new, fix8974_old
86
87
88 5
def read_os_files(localdir, core=False):
89
    """
90
    Read list of OS signed files, return name assignments.
91
92
    :param localdir: Directory to use.
93
    :type localdir: str
94
95
    :param core: If we're using a core OS image. Default is false.
96
    :type core: bool
97
    """
98 5
    fix8960, fixomap_new, fixomap_old, fix8930, fix8974_new, fix8974_old = generate_os_fixes(core)
99
    # 8960
100 5
    os_8960 = find_signed_file(fix8960, localdir, "8960 OS")
101
    # 8x30 (10.3.1 MR+)
102 5
    os_8x30 = find_signed_file(fix8930, localdir, "8x30 OS", True)
103 5
    if os_8x30 is None:
104 5
        os_8x30 = find_signed_file(fix8960, localdir, "8x30 OS")
105
    # 8974
106 5
    os_8974 = find_signed_file(fix8974_new, localdir, "8974 OS", True)
107 5
    if os_8974 is None:
108 5
        os_8974 = find_signed_file(fix8974_old, localdir, "8974 OS")
109
    # OMAP
110 5
    os_ti = find_signed_file(fixomap_new, localdir, "OMAP OS", True)
111 5
    if os_ti is None:
112 5
        os_ti = find_signed_file(fixomap_old, localdir, "OMAP OS")
113 5
    return [os_8960, os_8x30, os_8974, os_ti]
114
115
116 5
def read_radio_files(localdir):
117
    """
118
    Read list of radio signed files, return name assignments.
119
120
    :param localdir: Directory to use.
121
    :type localdir: str
122
    """
123
    # STL100-1
124 5
    radio_ti = find_signed_file("*radio.m5730*.signed", localdir, "OMAP radio")
125
    # STL100-X
126 5
    radio_z10 = find_signed_file("*radio.qc8960.BB*.signed", localdir, "8960 radio")
127
    # STL100-4
128 5
    radio_z10_vzw = find_signed_file("*radio.qc8960*omadm*.signed", localdir, "VZW 8960 radio")
129
    # Q10/Q5
130 5
    radio_q10 = find_signed_file("*radio.qc8960*wtr.*signed", localdir, "Q10/Q5 radio")
131
    # Z30/Classic
132 5
    radio_z30 = find_signed_file("*radio.qc8960*wtr5*.signed", localdir, "Z30/Classic radio")
133
    # Z3
134 5
    radio_z3 = find_signed_file("*radio.qc8930*wtr5*.signed", localdir, "Z3 radio")
135
    # Passport
136 5
    radio_8974 = find_signed_file("*radio.qc8974*wtr2*.signed", localdir, "Passport radio")
137 5
    return [radio_ti, radio_z10, radio_z10_vzw, radio_q10, radio_z30, radio_z3, radio_8974]
138
139
140 5
def zeropad(splitver, idx, padlen):
141
    """
142
    Zero-pad an element of an OS/radio version to a certain length.
143
144
    :param splitver: OS/radio version, but split into quarters.
145
    :type splitver: list(str)
146
147
    :param idx: Index of splitver which must be checked.
148
    :type idx: int
149
150
    :param padlen: Length to pad to.
151
    :type padlen: int
152
    """
153 5
    if len(splitver[idx]) < padlen:
154 5
        splitver[idx] = splitver[idx].rjust(padlen, "0")
155 5
    return splitver
156
157
158 5
def versionpad(splitver):
159
    """
160
    Properly pad an OS/radio version.
161
162
    :param splitver: OS/radio version, but split into quarters.
163
    :type splitver: list(str)
164
    """
165 5
    splitver = zeropad(splitver, 2, 2)
166 5
    splitver = zeropad(splitver, 3, 4)
167 5
    return splitver
168
169
170 5
def pretty_formatter(osversion, radioversion):
171
    """
172
    Format OS/radio versions to cope with systems with poor sorting.
173
174
    :param osversion: OS version, 10.x.y.zzzz.
175
    :type osversion: str
176
177
    :param radioversion: Radio version, 10.x.y.zzzz.
178
    :type radioversion: str
179
    """
180
    # 10.x.y.zzz becomes 10.x.0y.0zzz
181 5
    splitos = osversion.split(".")
182 5
    splitos = versionpad(splitos)
183 5
    the_os = ".".join(splitos)
184 5
    splitrad = radioversion.split(".")
185 5
    splitrad = versionpad(splitrad)
186 5
    the_radio = ".".join(splitrad)
187 5
    return the_os, the_radio
188
189
190 5
def format_suffix(altradio=None, radioversion=None, core=False):
191
    """
192
    Formulate suffix for hybrid autoloaders.
193
194
    :param altradio: If a hybrid autoloader is being made.
195
    :type altradio: bool
196
197
    :param radioversion: The hybrid radio version, if applicable.
198
    :type radioversion: str
199
200
    :param core: If we're using a core OS image. Default is false.
201
    :type core: bool
202
    """
203 5
    suffix = "_R{0}".format(radioversion) if altradio and radioversion else ""
204 5
    if core:
205 5
        suffix += "_CORE"
206 5
    return suffix
207
208
209 5
def generate_loaders(osversion, radioversion, radios=True, localdir=None, altradio=False, core=False):
210
    """
211
    Create and label autoloaders for :mod:`bbarchivist.scripts.archivist`.
212
213
    :param osversion: OS version, 10.x.y.zzzz.
214
    :type osversion: str
215
216
    :param radioversion: Radio version, 10.x.y.zzzz.
217
    :type radioversion: str
218
219
    :param radios: Whether to make radios or not. True by default.
220
    :type radios: bool
221
222
    :param localdir: Working path. Default is local dir.
223
    :type localdir: str
224
225
    :param altradio: If we're using an alternate radio. Default is false.
226
    :type altradio: bool
227
228
    :param core: If we're using a core OS image. Default is false.
229
    :type core: bool
230
    """
231
    # default parsing
232 5
    localdir = utilities.dirhandler(localdir, os.getcwd())
233 5
    print("GETTING FILENAMES...")
234 5
    filedict = read_files(localdir, core)
235 5
    osversion, radioversion = pretty_formatter(osversion, radioversion)
236 5
    suffix = format_suffix(altradio, radioversion, core)
237
    # Generate loaders
238 5
    print("CREATING LOADERS...")
239 5
    filtrad = [rad for rad in filedict.keys() if rad]  # pop None
240 5
    generate_individual_loaders(filtrad, osversion, radioversion, suffix, filedict, radios, localdir)
241
242
243 5
def generate_individual_loaders(filtrad, osversion, radioversion, suffix, filedict, radios, localdir):
244
    """
245
    Generate individual loaders when generating several at once.
246
247
    :param filtrad: List of radio files, if they exist.
248
    :type filtrad: list(str)
249
250
    :param osversion: OS version, 10.x.y.zzzz.
251
    :type osversion: str
252
253
    :param radioversion: Radio version, 10.x.y.zzzz.
254
    :type radioversion: str
255
256
    :param suffix: Alternate radio, or blank.
257
    :type suffix: str
258
259
    :param filedict: Dictionary of radio:OS pairs.
260
    :type filedict: dict(str: str)
261
262
    :param radios: Whether to make radios or not. True by default.
263
    :type radios: bool
264
265
    :param localdir: Working path. Default is local dir.
266
    :type localdir: str
267
    """
268 5
    for radval in filtrad:
269 5
        device = generate_device(radval)
270 5
        osname = generate_filename(device, osversion, suffix)
271 5
        osfile = filedict[radval]
272 5
        if osfile is not None:
273 5
            wrap_pseudocap(osname, localdir, osfile, radval)
274 5
        if radios:
275 5
            radname = generate_filename(device, radioversion, "")
276 5
            wrap_pseudocap(radname, localdir, radval)
277
278
279
280 5
def wrap_pseudocap(filename, folder, first, second=None):
281
    """
282
    A filtered, excepting wrapper for pseudocap.
283
284
    :param filename: The title of the new loader.
285
    :type filename: str
286
287
    :param folder: The folder to create the loader in.
288
    :type folder: str
289
290
    :param first: The first signed file, required.
291
    :type first: str
292
293
    :param second: The second signed file, optional.
294
    :type second: str
295
    """
296 5
    if first is None:
297 5
        print("No OS!")
298 5
        raise SystemError
299 5
    try:
300 5
        pseudocap.make_autoloader(filename, [first, second], folder=folder)
301 5
    except (OSError, IndexError, SystemError) as exc:
302 5
        msg = "Could not create {0}".format(filename)
303 5
        exceptions.handle_exception(exc, msg, None)
304
305
306 5
def generate_skeletons():
307
    """
308
    Read JSON to get a dict of all filename components.
309
    """
310 5
    namelist = {0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None}
311 5
    data = jsonutils.load_json('integermap')
312 5
    for key in data:
313 5
        if key['id'] in namelist:
314 5
            namelist[key['id']] = (key['parts'])
315 5
            namelist[key['id']].append(".exe")
316 5
    return namelist
317
318
319 5
def generate_device(radio):
320
    """
321
    Read JSON to get the device integer ID from device radio.
322
323
    :param radio: The radio filename to look up.
324
    :type radio: str
325
    """
326 5
    data = jsonutils.load_json('integermap')
327 5
    for key in data:
328 5
        if key['radtype'] in radio:
329 5
            idx = int(key['id'])
330 5
            break
331 5
    return idx
332
333
334 5
def generate_filename(device, version, suffix=None):
335
    """
336
    Use skeleton dict to create loader filenames.
337
338
    :param device: Device to use.
339
    :type device: int
340
341
    :param version: OS or radio version.
342
    :type version: str
343
344
    :param suffix: Alternate radio, or blank.
345
    :type suffix: str
346
    """
347 5
    thed = generate_skeletons()
348 5
    if device < 0:
349 5
        return None
350 5
    dev = thed[device]
351 5
    if suffix is None:
352 5
        suffix = ""
353 5
    return "{0}{1}{2}{3}{4}".format(dev[0], version, suffix, dev[1], dev[2])
354
355
356 5
def generate_lazy_loader(osversion, device, localdir=None, altradio=None, core=False):
357
    """
358
    Create and label autoloaders for :mod:`bbarchivist.scripts.lazyloader`.
359
    :func:`generate_loaders`, but for making one OS/radio loader.
360
361
    :param osversion: OS version, 10.x.y.zzzz.
362
    :type osversion: str
363
364
    :param device: Selected device, from
365
    :type device: int
366
367
    :param localdir: Working path. Default is local dir.
368
    :type localdir: str
369
370
    :param altradio: The alternate radio in use, if there is one.
371
    :type altradio: str
372
373
    :param core: If we're using a core OS image. Default is false.
374
    :type core: bool
375
    """
376
    # default parsing
377 5
    localdir = utilities.dirhandler(localdir, os.getcwd())
378 5
    print("CREATING LOADER...")
379 5
    suffix = format_suffix(bool(altradio), altradio, core)
380 5
    osfile = None
381 5
    absoglob = "{0}{1}".format(localdir, os.sep)
382 5
    try:
383 5
        osfile = str(glob.glob("{0}*_sfi*.signed".format(absoglob))[0])
384 5
    except IndexError:
385 5
        print("No OS found")
386
    else:
387 5
        generate_lazy_set(osversion, device, osfile, suffix, absoglob, localdir)
388
389
390 5
def generate_lazy_set(osversion, device, osfile, suffix, absoglob, localdir=None):
391
    """
392
    Get radio file and then generate autoloader.
393
394
    :param osversion: OS version, 10.x.y.zzzz.
395
    :type osversion: str
396
397
    :param device: Selected device, from
398
    :type device: int
399
400
    :param osfile: OS signed filename.
401
    :type osfile: str
402
403
    :param suffix: Loader name suffix.
404
    :type suffix: str
405
406
    :param absoglob: Local path + path separator.
407
    :type absoglob: str
408
409
    :param localdir: Working path. Default is local dir.
410
    :type localdir: str
411
    """
412 5
    radiofile = None
413 5
    try:
414 5
        sset = set(glob.glob("{0}*.signed".format(absoglob)))
415 5
        rset = sset - set(glob.glob("{0}*_sfi*.signed".format(absoglob)))
416 5
        radiofile = str(list(rset)[0])
417 5
    except IndexError:
418 5
        print("No radio found")
419
    else:
420 5
        loadername = generate_lazy_filename(osversion, suffix, device)
421 5
        wrap_pseudocap(loadername, localdir, osfile, radiofile)
422
423
424 5
def generate_lazy_filename(osversion, suffix, device):
425
    """
426
    Read JSON to formulate a single filename.
427
428
    :param osversion: OS version.
429
    :type osversion: str
430
431
    :param suffix: Alternate radio, or just blank.
432
    :type suffix: str
433
434
    :param device: Device to use.
435
    :type device: int
436
    """
437 5
    data = jsonutils.load_json('integermap')
438 5
    for key in data:
439 5
        if key['id'] == device:
440 5
            fname = key['parts']
441 5
            break
442
    return "{0}{1}{2}{3}.exe".format(fname[0], osversion, suffix, fname[1])
443