Completed
Push — master ( 1efb8b...96d2e8 )
by John
02:48
created

goargs()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 8
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
1
#!/usr/bin/env python3
2 4
"""This module is used for miscellaneous utilities."""
3
4 4
import os  # path work
5 4
import argparse  # argument parser for filters
6 4
import platform  # platform info
7 4
import glob  # cap grabbing
8 4
import hashlib  # base url creation
9 4
import threading  # get thread for spinner
10 4
import time  # spinner delay
11 4
import sys  # streams, version info
12 4
import itertools  # spinners gonna spin
13 4
import subprocess  # loader verification
14 4
from bbarchivist import bbconstants  # cap location, version, filename bits
15 4
from bbarchivist import compat  # backwards compat
16 4
from bbarchivist import dummy  # useless stdout
17 4
from bbarchivist import exceptions  # exceptions
18 4
from bbarchivist import iniconfig  # config parsing
19
20 4
__author__ = "Thurask"
21 4
__license__ = "WTFPL v2"
22 4
__copyright__ = "Copyright 2015-2016 Thurask"
23
24
25 4
def grab_cap():
26
    """
27
    Figure out where cap is, local, specified or system-supplied.
28
    """
29 4
    try:
30 4
        capfile = glob.glob(os.path.join(os.getcwd(), bbconstants.CAP.filename))[0]
31 4
    except IndexError:
32
        try:
33
            cappath = cappath_config_loader()
34
            capfile = glob.glob(cappath)[0]
35
        except IndexError:
36
            cappath_config_writer(bbconstants.CAP.location)
37
            return bbconstants.CAP.location  # no ini cap
38
        else:
39
            cappath_config_writer(os.path.abspath(capfile))
40
            return os.path.abspath(capfile)  # ini cap
41
    else:
42 4
        return os.path.abspath(capfile)  # local cap
43
44
45 4
def grab_cfp():
46
    """
47
    Figure out where cfp is, local or system-supplied.
48
    """
49 4
    try:
50 4
        cfpfile = glob.glob(os.path.join(os.getcwd(), bbconstants.CFP.filename))[0]
51 4
    except IndexError:
52 4
        cfpfile = bbconstants.CFP.location  # system cfp
53 4
    return os.path.abspath(cfpfile)  # local cfp
54
55
56 4
def new_enough(minver):
57
    """
58
    Check if we're at or above a minimum Python version.
59
60
    :param minver: Minimum Python version (3.minver).
61
    :type minver: int
62
    """
63 4
    return False if minver > sys.version_info[1] else True
64
65
66 4
def fsizer(file_size):
67
    """
68
    Raw byte file size to human-readable string.
69
70
    :param file_size: Number to parse.
71
    :type file_size: float
72
    """
73 4
    if file_size is None:
74 4
        file_size = 0
75 4
    fsize = float(file_size)
76 4
    for sfix in ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB']:
77 4
        if fsize < 1024.0:
78 4
            size = "{0:3.2f}{1}".format(fsize, sfix)
79 4
            break
80
        else:
81 4
            fsize /= 1024.0
82
    else:
83 4
        size = "{0:3.2f}{1}".format(fsize, 'YB')
84 4
    return size
85
86
87 4
def signed_file_args(files):
88
    """
89
    Check if there are between 1 and 6 files supplied to argparse.
90
91
    :param files: List of signed files, between 1 and 6 strings.
92
    :type files: list(str)
93
    """
94 4
    filelist = [file for file in files if file]
95 4
    if not 1 <= len(filelist) <= 6:
96 4
        raise argparse.ArgumentError(argument=None, message="Requires 1-6 signed files")
97 4
    return files
98
99
100 4
def file_exists(file):
101
    """
102
    Check if file exists, raise argparse error if it doesn't.
103
104
    :param file: Path to a file, including extension.
105
    :type file: str
106
    """
107 4
    if not os.path.exists(file):
108 4
        raise argparse.ArgumentError(argument=None, message="{0} not found.".format(file))
109 4
    return file
110
111
112 4
def positive_integer(input_int):
113
    """
114
    Check if number > 0, raise argparse error if it isn't.
115
116
    :param input_int: Integer to check.
117
    :type input_int: str
118
    """
119 4
    if int(input_int) <= 0:
120 4
        info = "{0} is not >=0.".format(str(input_int))
121 4
        raise argparse.ArgumentError(argument=None, message=info)
122 4
    return int(input_int)
123
124
125 4
def valid_method(method):
126
    """
127
    Check if compression method is valid, raise argparse error if it isn't.
128
129
    :param method: Compression method to check.
130
    :type method: str
131
    """
132 4
    methodlist = bbconstants.METHODS
133 4
    if not new_enough(3):
134 4
        methodlist = [x for x in bbconstants.METHODS if x != "txz"]
135 4
    if method not in methodlist:
136 4
        info = "Invalid method {0}.".format(method)
137 4
        raise argparse.ArgumentError(argument=None, message=info)
138 4
    return method
139
140
141 4
def valid_carrier(mcc_mnc):
142
    """
143
    Check if MCC/MNC is valid (1-3 chars), raise argparse error if it isn't.
144
145
    :param mcc_mnc: MCC/MNC to check.
146
    :type mcc_mnc: str
147
    """
148 4
    if not str(mcc_mnc).isdecimal():
149 4
        infod = "Non-integer {0}.".format(str(mcc_mnc))
150 4
        raise argparse.ArgumentError(argument=None, message=infod)
151 4
    if len(str(mcc_mnc)) > 3 or len(str(mcc_mnc)) == 0:
152 4
        infol = "{0} is invalid.".format(str(mcc_mnc))
153 4
        raise argparse.ArgumentError(argument=None, message=infol)
154
    else:
155 4
        return mcc_mnc
156
157
158 4
def escreens_pin(pin):
159
    """
160
    Check if given PIN is valid, raise argparse error if it isn't.
161
162
    :param pin: PIN to check.
163
    :type pin: str
164
    """
165 4
    if len(pin) == 8:
166 4
        try:
167 4
            int(pin, 16)  # hexadecimal-ness
168 4
        except ValueError:
169 4
            raise argparse.ArgumentError(argument=None, message="Invalid PIN.")
170
        else:
171 4
            return pin.lower()
172
    else:
173 4
        raise argparse.ArgumentError(argument=None, message="Invalid PIN.")
174
175
176 4
def escreens_duration(duration):
177
    """
178
    Check if Engineering Screens duration is valid.
179
180
    :param duration: Duration to check.
181
    :type duration: int
182
    """
183 4
    if int(duration) in (1, 3, 6, 15, 30):
184 4
        return int(duration)
185
    else:
186 4
        raise argparse.ArgumentError(argument=None, message="Invalid duration.")
187
188
189 4
def droidlookup_hashtype(method):
190
    """
191
    Check if Android autoloader lookup hash type is valid.
192
193
    :param method: None for regular OS links, "sha256/512" for SHA256 or 512 hash.
194
    :type method: str
195
    """
196 4
    if method.lower() in ("sha512", "sha256"):
197 4
        return method.lower()
198
    else:
199 4
        raise argparse.ArgumentError(argument=None, message="Invalid type.")
200
201
202 4
def droidlookup_devicetype(device):
203
    """
204
    Check if Android autoloader device type is valid.
205
206
    :param device: Android autoloader types to check.
207
    :type device: str
208
    """
209 4
    devices = ("Priv", "DTEK50", "DTEK60")
210 4
    if device is None:
211 4
        return None
212
    else:
213 4
        for dev in devices:
214 4
            if dev.lower() == device.lower():
215 4
                return dev
216 4
        raise argparse.ArgumentError(argument=None, message="Invalid device.")
217
218
219 4
def s2b(input_check):
220
    """
221
    Return Boolean interpretation of string input.
222
223
    :param input_check: String to check if it means True or False.
224
    :type input_check: str
225
    """
226 4
    return str(input_check).lower() in ("yes", "true", "t", "1", "y")
227
228
229 4
def is_amd64():
230
    """
231
    Check if script is running on an AMD64 system.
232
    """
233 4
    return platform.machine().endswith("64")
234
235
236 4
def is_windows():
237
    """
238
    Check if script is running on Windows.
239
    """
240 4
    return platform.system() == "Windows"
241
242
243 4
def talkaprint(msg, talkative=False):
244
    """
245
    Print only if asked to.
246
247
    :param msg: Message to print.
248
    :type msg: str
249
250
    :param talkative: Whether to output to screen. False by default.
251
    :type talkative: bool
252
    """
253 4
    if talkative:
254 4
        print(msg)
255
256
257 4
def get_seven_zip(talkative=False):
258
    """
259
    Return name of 7-Zip executable.
260
    On POSIX, it MUST be 7za.
261
    On Windows, it can be installed or supplied with the script.
262
    :func:`win_seven_zip` is used to determine if it's installed.
263
264
    :param talkative: Whether to output to screen. False by default.
265
    :type talkative: bool
266
    """
267 4
    return win_seven_zip(talkative) if is_windows() else "7za"
268
269
270 4
def win_seven_zip(talkative=False):
271
    """
272
    For Windows, check where 7-Zip is ("where", pretty much).
273
    Consult registry first for any installed instances of 7-Zip.
274
275
    :param talkative: Whether to output to screen. False by default.
276
    :type talkative: bool
277
    """
278
    talkaprint("CHECKING INSTALLED FILES...", talkative)
279
    try:
280
        import winreg  # windows registry
281
        hk7z = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\7-Zip")
282
        path = winreg.QueryValueEx(hk7z, "Path")
283
    except OSError as exc:
284
        if talkative:
285
            exceptions.handle_exception(exc, xit=None)
286
        talkaprint("TRYING LOCAL FILES...", talkative)
287
        return win_seven_zip_local(talkative)
288
    else:
289
        talkaprint("7ZIP USING INSTALLED FILES", talkative)
290
        return '"{0}"'.format(os.path.join(path[0], "7z.exe"))
291
292
293 4
def win_seven_zip_local(talkative=False):
294
    """
295
    If 7-Zip isn't in the registry, fall back onto supplied executables.
296
    If *those* aren't there, return "error".
297
298
    :param talkative: Whether to output to screen. False by default.
299
    :type talkative: bool
300
    """
301
    filecount = len([x for x in os.listdir(os.getcwd()) if x in ["7za.exe", "7z.exe"]])
302
    if filecount == 2:
303
        talkaprint("7ZIP USING LOCAL FILES", talkative)
304
        szexe = "7za.64.exe" if is_amd64() else "7za.exe"
305
    else:
306
        talkaprint("NO LOCAL FILES", talkative)
307
        szexe = "error"
308
    return szexe
309
310
311 4
def get_core_count():
312
    """
313
    Find out how many CPU cores this system has.
314
    """
315 4
    try:
316 4
        cores = str(compat.enum_cpus())  # 3.4 and up
317
    except NotImplementedError:
318
        cores = "1"  # 3.2-3.3
319
    else:
320 4
        if compat.enum_cpus() is None:
321
            cores = "1"
322 4
    return cores
323
324
325 4
def prep_seven_zip_path(path, talkative=False):
326
    """
327
    Print p7zip path on POSIX, or notify if not there.
328
329
    :param path: Path to use.
330
    :type path: str
331
332
    :param talkative: Whether to output to screen. False by default.
333
    :type talkative: bool
334
    """
335 4
    if path is None:
336 4
        talkaprint("NO 7ZIP\nPLEASE INSTALL p7zip", talkative)
337 4
        return False
338
    else:
339 4
        talkaprint("7ZIP FOUND AT {0}".format(path), talkative)
340 4
        return True
341
342
343 4
def prep_seven_zip_posix(talkative=False):
344
    """
345
    Check for p7zip on POSIX.
346
347
    :param talkative: Whether to output to screen. False by default.
348
    :type talkative: bool
349
    """
350 4
    try:
351 4
        path = compat.where_which("7za")
352 4
    except ImportError:
353 4
        talkaprint("PLEASE INSTALL SHUTILWHICH WITH PIP", talkative)
354 4
        return False
355
    else:
356 4
        return prep_seven_zip_path(path, talkative)
357
358
359 4
def prep_seven_zip(talkative=False):
360
    """
361
    Check for presence of 7-Zip.
362
    On POSIX, check for p7zip.
363
    On Windows, check for 7-Zip.
364
365
    :param talkative: Whether to output to screen. False by default.
366
    :type talkative: bool
367
    """
368 4
    if is_windows():
369
        return get_seven_zip(talkative) != "error"
370
    else:
371 4
        return prep_seven_zip_posix(talkative)
372
373
374 4
def increment(version, inc=3):
375
    """
376
    Increment version by given number. For repeated lookups.
377
378
    :param version: w.x.y.ZZZZ, becomes w.x.y.(ZZZZ + increment).
379
    :type version: str
380
381
    :param inc: What to increment by. Default is 3.
382
    :type inc: str
383
    """
384 4
    splitos = version.split(".")
385 4
    splitos[3] = int(splitos[3])
386 4
    if splitos[3] > 9996:  # prevent overflow
387 4
        splitos[3] = 0
388 4
    splitos[3] += int(inc)
389 4
    splitos[3] = str(splitos[3])
390 4
    return ".".join(splitos)
391
392
393 4
def stripper(name):
394
    """
395
    Strip fluff from bar filename.
396
397
    :param name: Bar filename, must contain '-nto+armle-v7+signed.bar'.
398
    :type name: str
399
    """
400 4
    return name.replace("-nto+armle-v7+signed.bar", "")
401
402
403 4
def create_base_url(softwareversion):
404
    """
405
    Make the root URL for production server files.
406
407
    :param softwareversion: Software version to hash.
408
    :type softwareversion: str
409
    """
410
    # Hash software version
411 4
    swhash = hashlib.sha1(softwareversion.encode('utf-8'))
412 4
    hashedsoftwareversion = swhash.hexdigest()
413
    # Root of all urls
414 4
    baseurl = "http://cdn.fs.sl.blackberry.com/fs/qnx/production/{0}".format(hashedsoftwareversion)
415 4
    return baseurl
416
417
418 4
def format_app_name(appname):
419
    """
420
    Convert long reverse DNS name to short name.
421
422
    :param appname: Application name (ex. sys.pim.calendar -> "calendar")
423
    :type appname: str
424
    """
425 4
    final = appname.split(".")[-1]
426 4
    return final
427
428
429 4
def create_bar_url(softwareversion, appname, appversion, clean=False):
430
    """
431
    Make the URL for any production server file.
432
433
    :param softwareversion: Software version to hash.
434
    :type softwareversion: str
435
436
    :param appname: Application name, preferably like on server.
437
    :type appname: str
438
439
    :param appversion: Application version.
440
    :type appversion: str
441
442
    :param clean: Whether or not to clean up app name. Default is False.
443
    :type clean: bool
444
    """
445 4
    baseurl = create_base_url(softwareversion)
446 4
    if clean:
447 4
        appname = format_app_name(appname)
448 4
    return "{0}/{1}-{2}-nto+armle-v7+signed.bar".format(baseurl, appname, appversion)
449
450
451 4
def generate_urls(softwareversion, osversion, radioversion, core=False):
452
    """
453
    Generate a list of OS URLs and a list of radio URLs based on input.
454
455
    :param softwareversion: Software version to hash.
456
    :type softwareversion: str
457
458
    :param osversion: OS version.
459
    :type osversion: str
460
461
    :param radioversion: Radio version.
462
    :type radioversion: str
463
464
    :param core: Whether or not to return core URLs as well.
465
    :type core: bool
466
    """
467 4
    osurls = [
468
        create_bar_url(softwareversion, "winchester.factory_sfi.desktop", osversion),
469
        create_bar_url(softwareversion, "qc8960.factory_sfi.desktop", osversion),
470
        create_bar_url(softwareversion, "qc8960.factory_sfi.desktop", osversion),
471
        create_bar_url(softwareversion, "qc8974.factory_sfi.desktop", osversion)
472
    ]
473 4
    radiourls = [
474
        create_bar_url(softwareversion, "m5730", radioversion),
475
        create_bar_url(softwareversion, "qc8960", radioversion),
476
        create_bar_url(softwareversion, "qc8960.omadm", radioversion),
477
        create_bar_url(softwareversion, "qc8960.wtr", radioversion),
478
        create_bar_url(softwareversion, "qc8960.wtr5", radioversion),
479
        create_bar_url(softwareversion, "qc8930.wtr5", radioversion),
480
        create_bar_url(softwareversion, "qc8974.wtr2", radioversion)
481
    ]
482 4
    coreurls = []
483 4
    splitos = [int(i) for i in osversion.split(".")]
484 4
    osurls[2] = filter_1031(osurls[2], splitos, 5)
485 4
    osurls[3] = filter_1031(osurls[3], splitos, 6)
486 4
    if core:
487 4
        coreurls = [x.replace(".desktop", "") for x in osurls]
488 4
    return osurls, radiourls, coreurls
489
490
491 4
def filter_1031(osurl, splitos, device):
492
    """
493
    Modify URLs to reflect changes in 10.3.1+.
494
495
    :param osurl: OS URL to modify.
496
    :type osurl: str
497
498
    :param splitos: OS version, split and cast to int: [10, 3, 2, 2876]
499
    :type splitos: list(int)
500
501
    :param device: Device to use.
502
    :type device: int
503
    """
504 4
    if (splitos[1] >= 4) or (splitos[1] == 3 and splitos[2] >= 1):
505 4
        if device == 5:
506 4
            osurl = osurl.replace("qc8960.factory_sfi", "qc8960.factory_sfi_hybrid_qc8x30")
507 4
        elif device == 6:
508 4
            osurl = osurl.replace("qc8974.factory_sfi", "qc8960.factory_sfi_hybrid_qc8974")
509 4
    return osurl
510
511
512 4
def generate_lazy_urls(softwareversion, osversion, radioversion, device):
513
    """
514
    Generate a pair of OS/radio URLs based on input.
515
516
    :param softwareversion: Software version to hash.
517
    :type softwareversion: str
518
519
    :param osversion: OS version.
520
    :type osversion: str
521
522
    :param radioversion: Radio version.
523
    :type radioversion: str
524
525
    :param device: Device to use.
526
    :type device: int
527
    """
528 4
    splitos = [int(i) for i in osversion.split(".")]
529 4
    rads = ["m5730", "qc8960", "qc8960.omadm", "qc8960.wtr",
530
            "qc8960.wtr5", "qc8930.wtr4", "qc8974.wtr2"]
531 4
    oses = ["winchester.factory", "qc8960.factory", "qc8960.verizon",
532
            "qc8974.factory"]
533 4
    maps = {0:0, 1:1, 2:2, 3:1, 4:1, 5:1, 6:3}
534 4
    osurl = create_bar_url(softwareversion, "{0}_sfi.desktop".format(oses[maps[device]]), osversion)
535 4
    radiourl = create_bar_url(softwareversion, rads[device], radioversion)
536 4
    osurl = filter_1031(osurl, splitos, device)
537 4
    return osurl, radiourl
538
539
540 4
def bulk_urls(softwareversion, osversion, radioversion, core=False, altsw=None):
541
    """
542
    Generate all URLs, plus extra Verizon URLs.
543
544
    :param softwareversion: Software version to hash.
545
    :type softwareversion: str
546
547
    :param osversion: OS version.
548
    :type osversion: str
549
550
    :param radioversion: Radio version.
551
    :type radioversion: str
552
553
    :param device: Device to use.
554
    :type device: int
555
556
    :param core: Whether or not to return core URLs as well.
557
    :type core: bool
558
559
    :param altsw: Radio software release, if not the same as OS.
560
    :type altsw: str
561
    """
562 4
    baseurl = create_base_url(softwareversion)
563 4
    osurls, radurls, coreurls = generate_urls(softwareversion, osversion, radioversion, core)
564 4
    vzwos, vzwrad = generate_lazy_urls(softwareversion, osversion, radioversion, 2)
565 4
    osurls.append(vzwos)
566 4
    radurls.append(vzwrad)
567 4
    vzwcore = vzwos.replace("sfi.desktop", "sfi")
568 4
    if core:
569 4
        coreurls.append(vzwcore)
570 4
    osurls = list(set(osurls))  # pop duplicates
571 4
    radurls = list(set(radurls))
572 4
    if core:
573 4
        coreurls = list(set(coreurls))
574 4
    if altsw is not None:
575 4
        altbase = create_base_url(altsw)
576 4
        radiourls2 = []
577 4
        for rad in radurls:
578 4
            radiourls2.append(rad.replace(baseurl, altbase))
579 4
        radurls = radiourls2
580 4
        del radiourls2
581 4
    return osurls, coreurls, radurls
582
583
584 4
def line_begin():
585
    """
586
    Go to beginning of line, to overwrite whatever's there.
587
    """
588 4
    sys.stdout.write("\r")
589 4
    sys.stdout.flush()
590
591
592 4
def spinner_clear():
593
    """
594
    Get rid of any spinner residue left in stdout.
595
    """
596 4
    sys.stdout.write("\b \b")
597 4
    sys.stdout.flush()
598
599
600 4
class Spinner(object):
601
    """
602
    A basic spinner using itertools. No need for progress.
603
    """
604
605 4
    def __init__(self):
606 4
        self.wheel = itertools.cycle(['-', '/', '|', '\\'])
607 4
        self.file = dummy.UselessStdout()
608
609 4
    def after(self):
610
        """
611
        Iterate over itertools.cycle, write to file.
612
        """
613 4
        try:
614 4
            self.file.write(next(self.wheel))
615 4
            self.file.flush()
616 4
            self.file.write("\b\r")
617 4
            self.file.flush()
618
        except (KeyboardInterrupt, SystemExit):
619
            self.stop()
620
621 4
    def stop(self):
622
        """
623
        Kill output.
624
        """
625 4
        self.file = dummy.UselessStdout()
626
627
628 4
class SpinManager(object):
629
    """
630
    Wraps around the itertools spinner, runs it in another thread.
631
    """
632
633 4
    def __init__(self):
634 4
        spinner = Spinner()
635 4
        self.spinner = spinner
636 4
        self.thread = threading.Thread(target=self.loop, args=())
637 4
        self.thread.daemon = True
638 4
        self.scanning = False
639 4
        self.spinner.file = dummy.UselessStdout()
640
641 4
    def start(self):
642
        """
643
        Begin the spinner.
644
        """
645 4
        self.spinner.file = sys.stderr
646 4
        self.scanning = True
647 4
        self.thread.start()
648
649 4
    def loop(self):
650
        """
651
        Spin if scanning, clean up if not.
652
        """
653 4
        while self.scanning:
654 4
            time.sleep(0.5)
655 4
            try:
656 4
                line_begin()
657 4
                self.spinner.after()
658
            except (KeyboardInterrupt, SystemExit):
659
                self.scanning = False
660
                self.stop()
661
662 4
    def stop(self):
663
        """
664
        Stop the spinner.
665
        """
666 4
        self.spinner.stop()
667 4
        self.scanning = False
668 4
        spinner_clear()
669 4
        line_begin()
670 4
        if not is_windows():
671 4
            print("\n")
672
673
674 4
def return_and_delete(target):
675
    """
676
    Read text file, then delete it. Return contents.
677
678
    :param target: Text file to read.
679
    :type target: str
680
    """
681 4
    with open(target, "r") as thefile:
682 4
        content = thefile.read()
683 4
    os.remove(target)
684 4
    return content
685
686
687 4
def verify_loader_integrity(loaderfile):
688
    """
689
    Test for created loader integrity. Windows-only.
690
691
    :param loaderfile: Path to loader.
692
    :type loaderfile: str
693
    """
694 4
    if not is_windows():
695 4
        pass
696
    else:
697 4
        excode = None
698 4
        try:
699 4
            with open(os.devnull, 'rb') as dnull:
700 4
                cmd = "{0} fileinfo".format(loaderfile)
701 4
                excode = subprocess.call(cmd, stdout=dnull, stderr=subprocess.STDOUT)
702 4
        except OSError:
703 4
            excode = -1
704 4
        return excode == 0  # 0 if OK, non-zero if something broke
705
706
707 4
def verify_bulk_loaders(ldir):
708
    """
709
    Run :func:`verify_loader_integrity` for all files in a dir.
710
711
    :param ldir: Directory to use.
712
    :type ldir: str
713
    """
714 4
    if not is_windows():
715 4
        pass
716
    else:
717 4
        files = [os.path.join(ldir, file) for file in os.listdir(ldir) if not os.path.isdir(file)]
718 4
        brokens = []
719 4
        for file in files:
720 4
            fname = os.path.basename(file)
721 4
            if fname.endswith(".exe") and fname.startswith(bbconstants.PREFIXES):
722 4
                print("TESTING: {0}".format(fname))
723 4
                if not verify_loader_integrity(file):
724 4
                    brokens.append(fname)
725 4
        return brokens
726
727
728 4
def workers(input_data):
729
    """
730
    Count number of CPU workers, smaller of number of threads and length of data.
731
732
    :param input_data: Input data, some iterable.
733
    :type input_data: list
734
    """
735 4
    runners = len(input_data) if len(input_data) < compat.enum_cpus() else compat.enum_cpus()
736 4
    return runners
737
738
739 4
def prep_logfile():
740
    """
741
    Prepare log file, labeling it with current date. Select folder based on frozen status.
742
    """
743 4
    logfile = "{0}.txt".format(time.strftime("%Y_%m_%d_%H%M%S"))
744 4
    root = os.getcwd() if getattr(sys, 'frozen', False) else os.path.expanduser("~")
745 4
    basefolder = os.path.join(root, "lookuplogs")
746 4
    os.makedirs(basefolder, exist_ok=True)
747 4
    record = os.path.join(basefolder, logfile)
748 4
    open(record, "w").close()
749 4
    return record
750
751
752 4
def prepends(file, pre, suf):
753
    """
754
    Check if filename starts with/ends with stuff.
755
756
    :param file: File to check.
757
    :type file: str
758
759
    :param pre: Prefix(es) to check.
760
    :type pre: str or list or tuple
761
762
    :param suf: Suffix(es) to check.
763
    :type suf: str or list or tuple
764
    """
765 4
    return file.startswith(pre) and file.endswith(suf)
766
767
768 4
def lprint(iterable):
769
    """
770
    A oneliner for 'for item in x: print item'.
771
772
    :param iterable: Iterable to print.
773
    :type iterable: list/tuple
774
    """
775 4
    for item in iterable:
776 4
        print(item)
777
778
779 4
def cappath_config_loader(homepath=None):
780
    """
781
    Read a ConfigParser file to get cap preferences.
782
783
    :param homepath: Folder containing ini file. Default is user directory.
784
    :type homepath: str
785
    """
786 4
    capini = iniconfig.generic_loader('cappath', homepath)
787 4
    cappath = capini.get('path', fallback=bbconstants.CAP.location)
788 4
    return cappath
789
790
791 4
def cappath_config_writer(cappath=None, homepath=None):
792
    """
793
    Write a ConfigParser file to store cap preferences.
794
795
    :param cappath: Method to use.
796
    :type cappath: str
797
798
    :param homepath: Folder containing ini file. Default is user directory.
799
    :type homepath: str
800
    """
801 4
    cappath = grab_cap() if cappath is None else cappath
802 4
    results = {"path": cappath}
803 4
    iniconfig.generic_writer("cappath", results, homepath)
804
805
806 4
def goargs(dirs):
807
    """
808
    Return prepared argument list for most instances of :func:`cond_check:.
809
810
    :param dirs: List of directories.
811
    :type dirs: list(str)
812
    """
813 4
    return [dirs[4], dirs[5], dirs[2], dirs[3]]
814
815
816 4
def cond_do(dofunc, goargs, restargs=None, condition=True):
0 ignored issues
show
Comprehensibility Bug introduced by
goargs is re-defining a name which is already available in the outer-scope (previously defined on line 806).

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...
817
    """
818
    Do a function, check a condition, then do same function but swap first argument.
819
820
    :param dofunc: Function to do.
821
    :type dofunc: function
822
823
    :param goargs: List of variable arguments.
824
    :type goargs: list(str)
825
826
    :param restargs: Rest of arguments, which are constant.
827
    :type restargs: list(str)
828
829
    :param condition: Condition to check in order to use secondarg.
830
    :type condition: bool
831
    """
832 4
    restargs = [] if restargs is None else restargs
833 4
    dofunc(goargs[0], *restargs)
834 4
    if condition:
835 4
        dofunc(goargs[1], *restargs)
836
837
838 4
def cond_check(dofunc, goargs, restargs=None, condition=True, checkif=True, checkifnot=True):
0 ignored issues
show
Comprehensibility Bug introduced by
goargs is re-defining a name which is already available in the outer-scope (previously defined on line 806).

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...
839
    """
840
    Do :func:`cond_do` based on a condition, then do it again based on a second condition.
841
842
    :param dofunc: Function to do.
843
    :type dofunc: function
844
845
    :param goargs: List of variable arguments.
846
    :type goargs: list(str)
847
848
    :param restargs: Rest of arguments, which are constant.
849
    :type restargs: list(str)
850
851
    :param condition: Condition to check in order to use secondarg.
852
    :type condition: bool
853
854
    :param checkif: Do :func:`cond_do` if this is True.
855
    :type checkif: bool
856
857
    :param checkifnot: Do :func:`cond_do` if this is False.
858
    :type checkifnot: bool
859
    """
860 4
    if checkif:
861 4
        cond_do(dofunc, goargs[0:2], restargs, condition)
862 4
    if not checkifnot:
863
        cond_do(dofunc, goargs[2:4], restargs, condition)
864