code: def parse_GSM(filepath, entry_name=None):
      
      if isinstance(filepath, str):
          with utils.smart_open(filepath) as f:
              soft = []
              has_table = False
              for line in f:
                  if "_table_begin" in line or (not line.startswith(("^", "!", "#"))):
                      has_table = True
                  soft.append(line.rstrip())
      else:
          soft = []
          has_table = False
          for line in filepath:
              if "_table_begin" in line or (not line.startswith(("^", "!", "#"))):
                  has_table = True
              soft.append(line.rstrip())
  
      if entry_name is None:
          sets = [i for i in soft if i.startswith("^")]
          if len(sets) > 1:
              raise Exception("More than one entry in GPL")
          if len(sets) == 0:
              raise NoEntriesException(
                  "No entries found. Check the if accession is correct!")
          entry_name = parse_entry_name(sets[0])
  
      columns = parse_columns(soft)
      metadata = parse_metadata(soft)
      if has_table:
          table_data = parse_table_data(soft)
      else:
          table_data = DataFrame()
  
      gsm = GSM(name=entry_name,
                table=table_data,
                metadata=metadata,
                columns=columns)
  
      return gsm
docs: Parse GSM entry from SOFT file.
  
      Args:
          filepath (:obj:`str` or :obj:`Iterable`): Path to file with 1 GSM entry
              or list of lines representing GSM from GSE file.
          entry_name (:obj:`str`, optional): Name of the entry. By default it is
              inferred from the data.
  
      Returns:
          :obj:`GEOparse.GSM`: A GSM object.