This method exports the SAS Data Set to a Pandas Data Frame, returning the Data Frame object.
      table   - the name of the SAS Data Set you want to export to a Pandas Data Frame
      libref  - the libref for the SAS Data Set.
      rowsep  - the row seperator character to use; defaults to '\n'
      colsep  - the column seperator character to use; defaults to '\t'

def sasdata2dataframe(self, table: str, libref: str ='', dsopts: dict = None, rowsep: str = '\x01', colsep: str = '\x02', **kwargs) -> '<Pandas Data Frame object>':
      """
      This method exports the SAS Data Set to a Pandas Data Frame, returning the Data Frame object.
      table   - the name of the SAS Data Set you want to export to a Pandas Data Frame
      libref  - the libref for the SAS Data Set.
      rowsep  - the row seperator character to use; defaults to '\n'
      colsep  - the column seperator character to use; defaults to '\t'
      """
      dsopts = dsopts if dsopts is not None else {}

      method = kwargs.pop('method', None)
      if method and method.lower() == 'csv':
         return self.sasdata2dataframeCSV(table, libref, dsopts, **kwargs)

      logf     = ''
      logn     = self._logcnt()
      logcodei = "%put E3969440A681A24088859985" + logn + ";"
      logcodeo = "\nE3969440A681A24088859985" + logn
      logcodeb =  logcodeo.encode()

      if libref:
         tabname = libref+"."+table
      else:
         tabname = table

      code  = "proc sql; create view sasdata2dataframe as select * from "+tabname+self._sb._dsopts(dsopts)+";quit;\n"
      code += "data _null_; file LOG; d = open('sasdata2dataframe');\n"
      code += "length var $256;\n"
      code += "lrecl = attrn(d, 'LRECL'); nvars = attrn(d, 'NVARS');\n"
      code += "lr='LRECL='; vn='VARNUMS='; vl='VARLIST='; vt='VARTYPE=';\n"
      code += "put lr lrecl; put vn nvars; put vl;\n"
      code += "do i = 1 to nvars; var = compress(varname(d, i), '00'x); put var; end;\n"
      code += "put vt;\n"
      code += "do i = 1 to nvars; var = vartype(d, i); put var; end;\n"
      code += "run;"

      ll = self.submit(code, "text")

      l2 = ll['LOG'].rpartition("LRECL= ")
      l2 = l2[2].partition("\n")
      lrecl = int(l2[0])

      l2 = l2[2].partition("VARNUMS= ")
      l2 = l2[2].partition("\n")
      nvars = int(l2[0])

      l2 = l2[2].partition("\n")
      varlist = l2[2].split("\n", nvars)
      del varlist[nvars]

      l2 = l2[2].partition("VARTYPE=")
      l2 = l2[2].partition("\n")
      vartype = l2[2].split("\n", nvars)
      del vartype[nvars]

      topts             = dict(dsopts)
      topts['obs']      = 0
      topts['firstobs'] = ''
   
      code  = "data work._n_u_l_l_;output;run;\n"
      code += "data _null_; set "+tabname+self._sb._dsopts(topts)+" work._n_u_l_l_;put 'FMT_CATS=';\n"
   
      for i in range(nvars):
         code += "_tom = vformatn('"+varlist[i]+"'n);put _tom;\n"
      code += "run;\nproc delete data=work._n_u_l_l_;run;"
   
      ll = self.submit(code, "text")
   
      l2 = ll['LOG'].rpartition("FMT_CATS=")
      l2 = l2[2].partition("\n")
      varcat = l2[2].split("\n", nvars)
      del varcat[nvars]

      rdelim = "'"+'%02x' % ord(rowsep.encode(self.sascfg.encoding))+"'x"
      cdelim = "'"+'%02x' % ord(colsep.encode(self.sascfg.encoding))+"'x "

      code = "data _null_; set "+tabname+self._sb._dsopts(dsopts)+";\n file "+self._tomods1.decode()+" dlm="+cdelim+" termstr=NL; put "

      for i in range(nvars):
         code += "'"+varlist[i]+"'n "
         if vartype[i] == 'N':
            if varcat[i] in self._sb.sas_date_fmts:
               code += 'E8601DA10. '+cdelim
            else:
               if varcat[i] in self._sb.sas_time_fmts:
                  code += 'E8601TM15.6 '+cdelim
               else:
                  if varcat[i] in self._sb.sas_datetime_fmts:
                     code += 'E8601DT26.6 '+cdelim
                  else:
                     code += 'best32. '+cdelim
         if not (i < (len(varlist)-1)):
            code += rdelim
      code += ";\n run;"

      ll = self._asubmit(code, 'text')

      self.stdin[0].send(b'\n'+logcodei.encode()+b'\n'+b'tom says EOL='+logcodeb+b'\n')


      BOM   = "\ufeff".encode()
      done  = False
      first = True
      datar = b''
      bail  = False
      r     = []
      df    = None
      trows = kwargs.get('trows', None)
      if not trows:
         trows = 100000

      while not done:
         while True:
             if os.name == 'nt':
                try:
                   rc = self.pid.wait(0)
                   self.pid = None
                   self._sb.SASpid = None
                   print('\nSAS process has terminated unexpectedly. RC from wait was: '+str(rc))
                   return None
                except:
                   pass
             else:
                rc = os.waitpid(self.pid, os.WNOHANG)
                if rc[1]:
                    self.pid = None
                    self._sb.SASpid = None
                    print('\nSAS process has terminated unexpectedly. RC from wait was: '+str(rc))
                    return None

             if bail:
                if datar.count(logcodeb) >= 1:
                   break
             try:
                data = self.stdout[0].recv(4096)
             except (BlockingIOError):
                data = b''

             if len(data) > 0:
                if first:
                   if data[0:3] == BOM:
                      data = data[3:len(data)]
                   first = False

                datar += data
                data   = datar.rpartition(colsep.encode()+rowsep.encode()+b'\n')
                datap  = data[0]+data[1]
                datar  = data[2]

                datap = datap.decode(self.sascfg.encoding, errors='replace')
                for i in datap.split(sep=colsep+rowsep+'\n'):
                   if i != '':
                      r.append(tuple(i.split(sep=colsep)))

                if len(r) > trows:
                   tdf = pd.DataFrame.from_records(r, columns=varlist)

                   for i in range(nvars):
                      if vartype[i] == 'N':
                         if varcat[i] not in self._sb.sas_date_fmts + self._sb.sas_time_fmts + self._sb.sas_datetime_fmts:
                            if tdf.dtypes[tdf.columns[i]].kind not in ('f','u','i','b','B','c','?'):
                               tdf[varlist[i]] = pd.to_numeric(tdf[varlist[i]], errors='coerce')
                         else:
                            if tdf.dtypes[tdf.columns[i]].kind not in ('M'):
                               tdf[varlist[i]] = pd.to_datetime(tdf[varlist[i]], errors='coerce')
                      else:
                         tdf[varlist[i]].replace(' ', np.NaN, True)

                   if df is not None:
                      df = df.append(tdf, ignore_index=True)
                   else:
                      df = tdf
                   r = []
             else:
                sleep(0.1)
                try:
                   log = self.stderr[0].recv(4096).decode(self.sascfg.encoding, errors='replace')
                except (BlockingIOError):
                   log = b''

                if len(log) > 0:
                   logf += log
                   if logf.count(logcodeo) >= 1:
                      bail = True
         done = True

      if len(r) > 0 or df is None:
         tdf = pd.DataFrame.from_records(r, columns=varlist)

         for i in range(nvars):
            if vartype[i] == 'N':
               if varcat[i] not in self._sb.sas_date_fmts + self._sb.sas_time_fmts + self._sb.sas_datetime_fmts:
                  if tdf.dtypes[tdf.columns[i]].kind not in ('f','u','i','b','B','c','?'):
                     tdf[varlist[i]] = pd.to_numeric(tdf[varlist[i]], errors='coerce')
               else:
                  if tdf.dtypes[tdf.columns[i]].kind not in ('M'):
                     tdf[varlist[i]] = pd.to_datetime(tdf[varlist[i]], errors='coerce')
            else:
               tdf[varlist[i]].replace(' ', np.NaN, True)

         if df is not None:
            df = df.append(tdf, ignore_index=True)
         else:
            df = tdf

      return df