Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c68c6b95 authored by Kelvin Zhang's avatar Kelvin Zhang
Browse files

Allow ParseOptions to compose multiple option parsers easily

There are certain options which we need to share in multiple binaries,
for example, the signer options. Current options parsing function only
accepts 1 extra option handler, which is inflexible. Extend it to take a
list of extra option handlers.

Currently, to add a new CLI flag, caller must append the flag name to
`extra_long_opts`, then pass an extra option handler which can handle
that option. Define a new dataclass which contains both the CLI flag
name and the code to handle that flag for better composition.

Test: th
Bug: 293313353
Change-Id: I758db66dfd95934f5b2701454d97bfe7d37dc16d
parent c7bc5399
Loading
Loading
Loading
Loading
+28 −5
Original line number Original line Diff line number Diff line
@@ -39,18 +39,23 @@ import tempfile
import threading
import threading
import time
import time
import zipfile
import zipfile

from typing import Iterable, Callable
from dataclasses import dataclass
from dataclasses import dataclass
from genericpath import isdir
from hashlib import sha1, sha256
from hashlib import sha1, sha256


import images
import images
import rangelib
import sparse_img
import sparse_img
from blockimgdiff import BlockImageDiff
from blockimgdiff import BlockImageDiff


logger = logging.getLogger(__name__)
logger = logging.getLogger(__name__)




@dataclass
class OptionHandler:
  extra_long_opts: Iterable[str]
  handler: Callable

class Options(object):
class Options(object):


  def __init__(self):
  def __init__(self):
@@ -2793,12 +2798,19 @@ def Usage(docstring):
def ParseOptions(argv,
def ParseOptions(argv,
                 docstring,
                 docstring,
                 extra_opts="", extra_long_opts=(),
                 extra_opts="", extra_long_opts=(),
                 extra_option_handler=None):
                 extra_option_handler: Iterable[OptionHandler] = None):
  """Parse the options in argv and return any arguments that aren't
  """Parse the options in argv and return any arguments that aren't
  flags.  docstring is the calling module's docstring, to be displayed
  flags.  docstring is the calling module's docstring, to be displayed
  for errors and -h.  extra_opts and extra_long_opts are for flags
  for errors and -h.  extra_opts and extra_long_opts are for flags
  defined by the caller, which are processed by passing them to
  defined by the caller, which are processed by passing them to
  extra_option_handler."""
  extra_option_handler."""
  extra_long_opts = list(extra_long_opts)
  if not isinstance(extra_option_handler, Iterable):
    extra_option_handler = [extra_option_handler]

  for handler in extra_option_handler:
    if isinstance(handler, OptionHandler):
      extra_long_opts.extend(handler.extra_long_opts)


  try:
  try:
    opts, args = getopt.getopt(
    opts, args = getopt.getopt(
@@ -2860,8 +2872,19 @@ def ParseOptions(argv,
    elif o in ("--logfile",):
    elif o in ("--logfile",):
      OPTIONS.logfile = a
      OPTIONS.logfile = a
    else:
    else:
      if extra_option_handler is None or not extra_option_handler(o, a):
      if extra_option_handler is None:
        assert False, "unknown option \"%s\"" % (o,)
        raise ValueError("unknown option \"%s\"" % (o,))
      success = False
      for handler in extra_option_handler:
        if isinstance(handler, OptionHandler):
          if handler.handler(o, a):
            success = True
            break
        elif handler(o, a):
          success = True
      if not success:
        raise ValueError("unknown option \"%s\"" % (o,))



  if OPTIONS.search_path:
  if OPTIONS.search_path:
    os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
    os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +