docs: def read(self, *names):  # type: ignore  # noqa: F811
          # type: (Any) -> Any
          r"""Read results from FORM.
  
          Wait for a response of FORM to obtain the results specified by
          the given names and return a corresponding string or (nested) list of
          strings. Objects to be read from FORM are expressions, $-variables and
          preprocessor variables.
  
          ========== =============================
            name       meaning
          ========== =============================
            "F"        expression F
            "$x"       $-variable $x
            "$x[]"     factorized $-variable $x
            "\`A'"     preprocessor variable A
          ========== =============================
  
          Note that the communication for the reading is performed within the
          preprocessor of FORM (i.e., at compile-time), so one may need to write
          ".sort" to get the correct result.
  
          If non-string objects are passed, they are considered as sequences, and
          the return value becomes a list corresponding to the arguments. If
          a sequence is passed as the argument to this method, it is guaranteed
          that the return value is always a list:
  
          >>> import form
          >>> f = form.open()
          >>> f.write('''
          ...     S a1,...,a3;
          ...     L F1 = a1;
          ...     L F2 = a2;
          ...     L F3 = a3;
          ...     .sort
          ... ''')
  
          >>> f.read(['F1'])
          ['a1']
          >>> f.read(['F1', 'F2'])
          ['a1', 'a2']
          >>> f.read(['F1', 'F2', 'F3'])
          ['a1', 'a2', 'a3']
  
          A more complicated example, which returns a nested list, is
  
          >>> f.read('F1', ['F2', 'F3'])
          ['a1', ['a2', 'a3']]
  
          >>> f.close()
          """
          if self._closed:
              raise IOError('tried to read from closed connection')
  
          if len(names) == 1 and not isinstance(names[0], string_types):
              names = tuple(names[0])
              if len(names) == 1:
                  return [self.read(*names)]  # Guarantee to return a list.
              else:
                  return self.read(*names)
  
          if any(not isinstance(x, string_types) for x in names):
              return [self.read(x) for x in names]
  
          assert self._parentin is not None
          assert self._parentout is not None
          assert self._loggingin is not None
  
          for e in names:
              if len(e) >= 2 and e[0] == '`' and e[-1] == "'":
                  self._parentout.write(
                      '#toexternal "{0}{1}"\n'.format(e, self._END_MARK))
              elif len(e) >= 3 and e[0] == '$' and e[-2:] == '[]':
                  # Special syntax "$x[]" for factorized $-variables.
                  # NOTE: (1) isfactorized($x) is zero when $x is 0 or $x has
                  #           only one factor even after FactArg is performed.
                  #       (2) `$x[0]' is accessible even if FactArg has not been
                  #           performed. Use `$x[0]' rather than
                  #           `isfactorized($x)`.
                  #       (3) `$x[1]' is not accessible (segfault) with versions
                  #           before Sep  3 2015, if $x has only one factor and
                  #           `$x[0]' gives 1.
                  self._parentout.write((
                      "#if `${0}[0]'\n"
                      "#toexternal \"(%$)\",${0}[1]\n"
                      "#do i=2,`${0}[0]'\n"
                      "#toexternal \"*(%$)\",${0}[`i']\n"
                      "#enddo\n"
                      "#else\n"
                      "#if termsin(${0})\n"
                      "#toexternal \"%$\",${0}\n"
                      "#else\n"
                      "#toexternal \"(0)\"\n"
                      "#endif\n"
                      "#endif\n"
                      "#toexternal \"{1}\"\n"
                  ).format(e[1:-2], self._END_MARK))
              elif len(e) >= 1 and e[0] == '$':
                  self._parentout.write(
                      '#toexternal "%${1}",{0}\n'.format(e, self._END_MARK))
              else:
                  self._parentout.write(
                      '#toexternal "%E{1}",{0}\n'.format(e, self._END_MARK))
          self._parentout.write('#redefine FORMLINKLOOPVAR "0"')
          self._parentout.write(self._PROMPT)
          self._parentout.flush()
  
          result = []
          out = self._parentin.read0()
          out_start = 0  # start position for searching _END_MARK.
          for _e in names:
              while True:
                  i = out.find(self._END_MARK, out_start)
                  if i >= 0:
                      result.append(out[:i])
                      out = out[i + self._END_MARK_LEN:]
                      out_start = 0
                      break
                  out_start = max(len(out) - self._END_MARK_LEN, 0)
  
                  r, _, _ = select.select((self._parentin, self._loggingin),
                                          (), ())
                  if self._loggingin in r:
                      s = self._loggingin.read()
                      if s:
                          i = s.rfind('\n')
                          if i >= 0:
                              msgs = s[:i].split('\n')
                              if self._log is not None:
                                  self._log.extend(msgs)
                              for msg in msgs:
                                  if (msg.find('-->') >= 0 or
                                          msg.find('==>') >= 0):
                                      if self._log:
                                          msg += '\n'
                                          msg += '\n'.join(self._log)
                                      self.close()
                                      raise FormError(msg)
                          self._loggingin.unread(s[i + 1:])
                  if self._parentin in r:
                      out += (self._parentin.read()
                              .replace('\n', '')
                              .replace('\\', '')
                              .replace(' ', ''))
  
          self._parentin.unread(out)
  
          if len(names) == 0:
              return None
          elif len(names) == 1:
              return result[0]
          else:
              return result