Source code for bbarchivist.argutils

#!/usr/bin/env python3
"""This module is used for argument utilities."""

import argparse  # argument parser for filters
import glob  # file lookup
import os  # path work
import subprocess  # running cfp/cap
import sys  # getattr

from bbarchivist import bbconstants  # constants
from bbarchivist import utilities  # version checking

__author__ = "Thurask"
__license__ = "WTFPL v2"
__copyright__ = "2018 Thurask"


[docs]def signed_file_args(files): """ Check if there are between 1 and 6 files supplied to argparse. :param files: List of signed files, between 1 and 6 strings. :type files: list(str) """ filelist = [file for file in files if file] if not 1 <= len(filelist) <= 6: raise argparse.ArgumentError(argument=None, message="Requires 1-6 signed files") return files
[docs]def file_exists(file): """ Check if file exists, raise argparse error if it doesn't. :param file: Path to a file, including extension. :type file: str """ if not os.path.exists(file): raise argparse.ArgumentError(argument=None, message="{0} not found.".format(file)) return file
[docs]def positive_integer(input_int): """ Check if number > 0, raise argparse error if it isn't. :param input_int: Integer to check. :type input_int: str """ if int(input_int) <= 0: info = "{0} is not >=0.".format(str(input_int)) raise argparse.ArgumentError(argument=None, message=info) return int(input_int)
[docs]def valid_method_poptxz(methodlist): """ Remove .tar.xz support if system is too old. :param methodlist: List of all methods. :type methodlist: tuple(str) """ if not utilities.new_enough(3): methodlist = [x for x in bbconstants.METHODS if x != "txz"] return methodlist
[docs]def valid_method(method): """ Check if compression method is valid, raise argparse error if it isn't. :param method: Compression method to check. :type method: str """ methodlist = bbconstants.METHODS methodlist = valid_method_poptxz(methodlist) if method not in methodlist: info = "Invalid method {0}.".format(method) raise argparse.ArgumentError(argument=None, message=info) return method
[docs]def valid_carrier(mcc_mnc): """ Check if MCC/MNC is valid (1-3 chars), raise argparse error if it isn't. :param mcc_mnc: MCC/MNC to check. :type mcc_mnc: str """ if not str(mcc_mnc).isdecimal(): infod = "Non-integer {0}.".format(str(mcc_mnc)) raise argparse.ArgumentError(argument=None, message=infod) if len(str(mcc_mnc)) > 3 or not str(mcc_mnc): infol = "{0} is invalid.".format(str(mcc_mnc)) raise argparse.ArgumentError(argument=None, message=infol) else: return mcc_mnc
[docs]def escreens_pin(pin): """ Check if given PIN is valid, raise argparse error if it isn't. :param pin: PIN to check. :type pin: str """ if len(pin) == 8: try: int(pin, 16) # hexadecimal-ness except ValueError: raise argparse.ArgumentError(argument=None, message="Invalid PIN.") else: return pin.lower() else: raise argparse.ArgumentError(argument=None, message="Invalid PIN.")
[docs]def escreens_duration(duration): """ Check if Engineering Screens duration is valid. :param duration: Duration to check. :type duration: int """ if int(duration) in (1, 3, 6, 15, 30): return int(duration) else: raise argparse.ArgumentError(argument=None, message="Invalid duration.")
[docs]def droidlookup_hashtype(method): """ Check if Android autoloader lookup hash type is valid. :param method: None for regular OS links, "sha256/512" for SHA256 or 512 hash. :type method: str """ if method.lower() in ("sha512", "sha256"): return method.lower() else: raise argparse.ArgumentError(argument=None, message="Invalid type.")
[docs]def droidlookup_devicetype(device): """ Check if Android autoloader device type is valid. :param device: Android autoloader types to check. :type device: str """ devices = ("Priv", "DTEK50", "DTEK60", "KEYone", "Aurora", "Motion", "KEY2", "KEY2LE") if device is None: return None else: for dev in devices: if dev.lower() == device.lower(): return dev raise argparse.ArgumentError(argument=None, message="Invalid device.")
[docs]def shortversion(): """ Get short app version (Git tag). """ if not getattr(sys, 'frozen', False): ver = bbconstants.VERSION else: verfile = glob.glob(os.path.join(os.getcwd(), "version.txt"))[0] with open(verfile) as afile: ver = afile.read() return ver
[docs]def longversion(): """ Get long app version (Git tag + commits + hash). """ if not getattr(sys, 'frozen', False): ver = (bbconstants.LONGVERSION, bbconstants.COMMITDATE) else: verfile = glob.glob(os.path.join(os.getcwd(), "longversion.txt"))[0] with open(verfile) as afile: ver = afile.read().split("\n") return ver
[docs]def slim_preamble(appname): """ Standard app name header. :param appname: Name of app. :type appname: str """ print("~~~{0} VERSION {1}~~~".format(appname.upper(), shortversion()))
[docs]def standard_preamble(appname, osversion, softwareversion, radioversion, altsw=None): """ Standard app name, OS, radio and software (plus optional radio software) print block. :param appname: Name of app. :type appname: str :param osversion: OS version, 10.x.y.zzzz. Required. :type osversion: str :param radioversion: Radio version, 10.x.y.zzzz. Can be guessed. :type radioversion: str :param softwareversion: Software release, 10.x.y.zzzz. Can be guessed. :type softwareversion: str :param altsw: Radio software release, if not the same as OS. :type altsw: str """ slim_preamble(appname) print("OS VERSION: {0}".format(osversion)) print("OS SOFTWARE VERSION: {0}".format(softwareversion)) print("RADIO VERSION: {0}".format(radioversion)) if altsw is not None: print("RADIO SOFTWARE VERSION: {0}".format(altsw))
[docs]def default_parser_vers(vers=None): """ Prepare version for default parser. :param vers: Versions: [git commit hash, git commit date] :param vers: list(str) """ if vers is None: vers = longversion() return vers
[docs]def default_parser_flags(parser, flags=None): """ Handle flags for default parser. :param parser: Parser to modify. :type parser: argparse.ArgumentParser :param flags: Tuple of sections to add. :type flags: tuple(str) """ if flags is not None: parser = dpf_flags_folder(parser, flags) parser = dpf_flags_osr(parser, flags) return parser
[docs]def dpf_flags_folder(parser, flags=None): """ Add generic folder flag to parser. :param parser: Parser to modify. :type parser: argparse.ArgumentParser :param flags: Tuple of sections to add. :type flags: tuple(str) """ if "folder" in flags: parser.add_argument("-f", "--folder", dest="folder", help="Working folder", default=None, metavar="DIR", type=file_exists) return parser
[docs]def dpf_flags_osr(parser, flags=None): """ Add generic OS/radio/software flags to parser. :param parser: Parser to modify. :type parser: argparse.ArgumentParser :param flags: Tuple of sections to add. :type flags: tuple(str) """ if "osr" in flags: parser.add_argument("os", help="OS version") parser.add_argument("radio", help="Radio version, 10.x.y.zzzz", nargs="?", default=None) parser.add_argument("swrelease", help="Software version, 10.x.y.zzzz", nargs="?", default=None) return parser
[docs]def default_parser(name=None, desc=None, flags=None, vers=None): """ A generic form of argparse's ArgumentParser. :param name: App name. :type name: str :param desc: App description. :type desc: str :param flags: Tuple of sections to add. :type flags: tuple(str) :param vers: Versions: [git commit hash, git commit date] :param vers: list(str) """ vers = default_parser_vers(vers) homeurl = "https://github.com/thurask/bbarchivist" parser = argparse.ArgumentParser(prog=name, description=desc, epilog=homeurl) parser.add_argument("-v", "--version", action="version", version="{0} {1} committed {2}".format(parser.prog, vers[0], vers[1])) parser = default_parser_flags(parser, flags) return parser
[docs]def generic_windows_shim(scriptname, scriptdesc, target, version): """ Generic CFP/CAP runner; Windows only. :param scriptname: Script name, 'bb-something'. :type scriptname: str :param scriptdesc: Script description, i.e. scriptname -h. :type scriptdesc: str :param target: Path to file to execute. :type target: str :param version: Version of target. :type version: str """ parser = default_parser(scriptname, scriptdesc) capver = "|{0}".format(version) parser = external_version(parser, capver) parser.parse_known_args(sys.argv[1:]) if utilities.is_windows(): subprocess.call([target] + sys.argv[1:]) else: print("Sorry, Windows only.")
[docs]def arg_verify_none(argval, message): """ Check if an argument is None, error out if it is. :param argval: Argument to check. :type argval: str :param message: Error message to print. :type message: str """ if argval is None: raise argparse.ArgumentError(argument=None, message=message)
[docs]def external_version(parser, addition): """ Modify the version string of argparse.ArgumentParser, adding something. :param parser: Parser to modify. :type parser: argparse.ArgumentParser :param addition: What to add. :type addition: str """ verarg = [arg for arg in parser._actions if isinstance(arg, argparse._VersionAction)][0] verarg.version = "{1}{0}".format(addition, verarg.version) return parser