Completed
Push — master ( 3186b0...1efb8b )
by John
02:40
created

cond_do()   A

Complexity

Conditions 2

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 19
ccs 4
cts 4
cp 1
crap 2
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 cond_do(dofunc, goargs, restargs=[], condition=True):
0 ignored issues
show
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
807
    """
808
    Do a function, check a condition, then do same function but swap first argument.
809
810
    :param dofunc: Function to do.
811
    :type dofunc: function
812
813
    :param goargs: List of variable arguments.
814
    :type goargs: list(str)
815
816
    :param restargs: Rest of arguments, which are constant.
817
    :type restargs: list(str)
818
819
    :param condition: Condition to check in order to use secondarg.
820
    :type condition: bool
821
    """
822 4
    dofunc(goargs[0], *restargs)
823 4
    if condition:
824 4
        dofunc(goargs[1], *restargs)
825
826
827 4
def cond_check(dofunc, goargs, restargs=[], condition=True, checkif=True, checkifnot=True):
0 ignored issues
show
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
828
    """
829
    Do :func:`cond_do` based on a condition, then do it again based on a second condition.
830
831
    :param dofunc: Function to do.
832
    :type dofunc: function
833
834
    :param goargs: List of variable arguments.
835
    :type goargs: list(str)
836
837
    :param restargs: Rest of arguments, which are constant.
838
    :type restargs: list(str)
839
840
    :param condition: Condition to check in order to use secondarg.
841
    :type condition: bool
842
843
    :param checkif: Do :func:`cond_do` if this is True.
844
    :type checkif: bool
845
846
    :param checkifnot: Do :func:`cond_do` if this is False.
847
    :type checkifnot: bool
848
    """
849 4
    if checkif:
850 4
        cond_do(dofunc, goargs[0:2], restargs, condition)
851
    if not checkifnot:
852
        cond_do(dofunc, goargs[2:4], restargs, condition)
0 ignored issues
show
Coding Style introduced by
Final newline missing
Loading history...