Calls osquery with given query and returns its output.

  Args:
    args: A query to call osquery with.

  Returns:
    A "parsed JSON" representation of the osquery output.

  Raises:
    QueryError: If the query is incorrect.
    TimeoutError: If a call to the osquery executable times out.
    Error: If anything else goes wrong with the subprocess call.

def Query(args):
  """Calls osquery with given query and returns its output.

  Args:
    args: A query to call osquery with.

  Returns:
    A "parsed JSON" representation of the osquery output.

  Raises:
    QueryError: If the query is incorrect.
    TimeoutError: If a call to the osquery executable times out.
    Error: If anything else goes wrong with the subprocess call.
  """
  query = args.query.encode("utf-8")
  timeout = args.timeout_millis / 1000  # `subprocess.run` uses seconds.
  # TODO: pytype is not aware of the backport.
  # pytype: disable=module-attr
  try:
    # We use `--S` to enforce shell execution. This is because on Windows there
    # is only `osqueryd` and `osqueryi` is not available. However, by passing
    # `--S` we can make `osqueryd` behave like `osqueryi`. Since this flag also
    # works with `osqueryi`, by passing it we simply expand number of supported
    # executable types.
    command = [config.CONFIG["Osquery.path"], "--S", "--json", query]
    proc = subprocess.run(
        command,
        timeout=timeout,
        check=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
  # TODO: Since we use a backported API, `SubprocessError` is hard
  # to work with. Until support for Python 2 is dropped we re-raise with simpler
  # exception type because we don't really care that much (the exception message
  # should be detailed enough anyway).
  except subprocess.TimeoutExpired as error:
    raise TimeoutError(cause=error)
  except subprocess.CalledProcessError as error:
    raise Error("osquery invocation error", cause=error)
  # pytype: enable=module-attr

  stdout = proc.stdout.decode("utf-8")
  stderr = proc.stderr.decode("utf-8").strip()
  return ProcOutput(stdout=stdout, stderr=stderr)