docs: def columns_in_formula(formula):
      """
      Returns the names of all the columns used in a patsy formula.
  
      Parameters
      ----------
      formula : str, iterable, or dict
          Any formula construction supported by ``str_model_expression``.
  
      Returns
      -------
      columns : list of str
  
      """
      if formula is None:
          return []
  
      formula = str_model_expression(formula, add_constant=False)
      columns = []
  
      tokens = map(
          lambda x: x.extra,
          tz.remove(
              lambda x: x.extra is None,
              _tokens_from_patsy(patsy.parse_formula.parse_formula(formula))))
  
      for tok in tokens:
          # if there are parentheses in the expression we
          # want to drop them and everything outside
          # and start again from the top
          if '(' in tok:
              start = tok.find('(') + 1
              fin = tok.rfind(')')
              columns.extend(columns_in_formula(tok[start:fin]))
          else:
              for toknum, tokval, _, _, _ in generate_tokens(
                      StringIO(tok).readline):
                  if toknum == NAME:
                      columns.append(tokval)
  
      return list(tz.unique(columns))