docs: def parse(self, line, cell, namespace=None):
      """Parses a line and cell into a dictionary of arguments, expanding variables from a namespace.
  
      For each line parameters beginning with --, it also checks the cell content and see if it exists
      there. For example, if "--config1" is a line parameter, it checks to see if cell dict contains
      "config1" item, and if so, use the cell value. The "config1" item will also be removed from
      cell content.
  
      Args:
        line: line content.
        cell: cell content.
        namespace: user namespace. If None, IPython's user namespace is used.
  
      Returns:
        A tuple of: 1. parsed config dict. 2. remaining cell after line parameters are extracted.
      """
  
      if namespace is None:
        ipy = IPython.get_ipython()
        namespace = ipy.user_ns
  
      # Find which subcommand in the line by comparing line with subcommand progs.
      # For example, assuming there are 3 subcommands with their progs
      #   %bq tables
      #   %bq tables list
      #   %bq datasets
      # and the line is "tables list --dataset proj.myds"
      # it will find the second one --- "tables list" because it matches the prog and
      # it is the longest.
      args = CommandParser.create_args(line, namespace)
  
      # "prog" is a ArgumentParser's path splitted by namspace, such as '%bq tables list'.
      sub_parsers_progs = [x.prog for x in self._get_subparsers()]
      matched_progs = []
      for prog in sub_parsers_progs:
        # Remove the leading magic such as "%bq".
        match = prog.split()[1:]
        for i in range(len(args)):
          if args[i:i + len(match)] == match:
            matched_progs.append(prog)
            break
  
      matched_prog = None
      if matched_progs:
        # Get the longest match.
        matched_prog = max(matched_progs, key=lambda x: len(x.split()))
  
      # Line args can be provided in cell too. If they are in cell, move them to line
      # so we can parse them all together.
      line_args = self._get_subparser_line_args(matched_prog)
      if line_args:
        cell_config = None
        try:
          cell_config, cell = google.datalab.utils.commands.parse_config_for_selected_keys(
              cell, line_args)
        except:
          # It is okay --- probably because cell is not in yaml or json format.
          pass
  
        if cell_config:
          google.datalab.utils.commands.replace_vars(cell_config, namespace)
          for arg_name in cell_config:
            arg_value = cell_config[arg_name]
            if arg_value is None:
              continue
  
            if '--' + arg_name in args:
              raise ValueError('config item "%s" is specified in both cell and line.' % arg_name)
            if isinstance(arg_value, bool):
              if arg_value:
                line += ' --%s' % arg_name
            else:
              line += ' --%s %s' % (arg_name, str(cell_config[arg_name]))
  
      # Parse args again with the new line.
      args = CommandParser.create_args(line, namespace)
      args = vars(self.parse_args(args))
  
      # Parse cell args.
      cell_config = None
      cell_args = self._get_subparser_cell_args(matched_prog)
      if cell_args:
        try:
          cell_config, _ = google.datalab.utils.commands.parse_config_for_selected_keys(
              cell, cell_args)
        except:
          # It is okay --- probably because cell is not in yaml or json format.
          pass
  
        if cell_config:
          google.datalab.utils.commands.replace_vars(cell_config, namespace)
  
        for arg in cell_args:
          if (cell_args[arg]['required'] and
             (cell_config is None or cell_config.get(arg, None) is None)):
            raise ValueError('Cell config "%s" is required.' % arg)
  
      if cell_config:
        args.update(cell_config)
  
      return args, cell