docs: def parse_function_names(sourcecode, top_level=True, ignore_condition=1):
      """
      Finds all function names in a file without importing it
  
      Args:
          sourcecode (str):
  
      Returns:
          list: func_names
  
      CommandLine:
          python -m utool.util_inspect parse_function_names
  
      Example:
          >>> # ENABLE_DOCTEST
          >>> from utool.util_inspect import *  # NOQA
          >>> import utool as ut
          >>> fpath = ut.util_inspect.__file__.replace('.pyc', '.py')
          >>> #fpath = ut.truepath('~/code/bintrees/bintrees/avltree.py')
          >>> sourcecode = ut.readfrom(fpath)
          >>> func_names = parse_function_names(sourcecode)
          >>> result = ('func_names = %s' % (ut.repr2(func_names),))
          >>> print(result)
      """
      import ast
      import utool as ut
      func_names = []
      if six.PY2:
          sourcecode = ut.ensure_unicode(sourcecode)
          encoded = sourcecode.encode('utf8')
          pt = ast.parse(encoded)
      else:
          pt = ast.parse(sourcecode)
  
      class FuncVisitor(ast.NodeVisitor):
  
          def __init__(self):
              super(FuncVisitor, self).__init__()
              self.condition_names = None
              self.condition_id = -9001
              self.in_condition_chain = False
  
          def visit_If(self, node):
              if ignore_condition:
                  return
              # if ignore_conditional:
              #     return
              # Ignore the main statement
              # print('----')
              # print('node.test = {!r}'.format(node.test))
              # print('node.orelse = {!r}'.format(node.orelse))
              if _node_is_main_if(node):
                  return
  
              # if isinstance(node.orelse, ast.If):
              #     # THIS IS AN ELIF
              #     self.condition_id += 1
              #     self.in_condition_chain = True
              #     ast.NodeVisitor.generic_visit(self, node)
              #     self.in_condition_chain = False
              #     pass
              # # TODO: where does else get parsed exactly?
  
              # Reset the set of conditionals
              # self.condition_id = 0
              # self.condition_names = ut.ddict(list)
  
              # self.in_condition_chain = True
              ast.NodeVisitor.generic_visit(self, node)
              # self.in_condition_chain = False
  
              # if False:
              #     # IF THIS WAS AN ELSE:
              #     if self.condition_names is not None:
              #         # anything defined in all conditions is kosher
              #         from six.moves import reduce
              #         common_names = reduce(set.intersection,
              #                               map(set, self.condition_names.values()))
              #         self.func_names.extend(common_names)
              #         self.condition_names = None
  
          def visit_FunctionDef(self, node):
              # if self.in_condition_chain and self.condition_names is not None:
              #     # dont immediately add things in conditions. Wait until we can
              #     # ensure which definitions are common in all conditions.
              #     self.condition_names[self.condition_id].append(node.name)
              # else:
              func_names.append(node.name)
              if not top_level:
                  ast.NodeVisitor.generic_visit(self, node)
  
          def visit_ClassDef(self, node):
              if not top_level:
                  ast.NodeVisitor.generic_visit(self, node)
      try:
          FuncVisitor().visit(pt)
      except Exception:
          raise
          pass
      return func_names