docs: def exec_func_src(func, globals_=None, locals_=None, key_list=None,
                    sentinal=None, update=None, keys=None, verbose=False,
                    start=None, stop=None):
      """
      execs a func and returns requested local vars.
  
      Does not modify globals unless update=True (or in IPython)
  
      SeeAlso:
          ut.execstr_funckw
      """
      if keys is None:
          keys = key_list
      import utool as ut
      sourcecode = ut.get_func_sourcecode(func, stripdef=True, stripret=True)
      if update is None:
          update = ut.inIPython()
      if globals_ is None:
          globals_ = ut.get_parent_frame().f_globals
      if locals_ is None:
          locals_ = ut.get_parent_frame().f_locals
      if sentinal is not None:
          sourcecode = ut.replace_between_tags(sourcecode, '', sentinal)
      if start is not None or stop is not None:
          sourcecode = '\n'.join(sourcecode.splitlines()[slice(start, stop)])
      globals_new = globals_.copy()
      if locals_ is not None:
          globals_new.update(locals_)
          # globals_new.update({k: v for k, v in locals_.items()
          #                     if k not in globals_new})
      orig_globals = globals_new.copy()
      #six.exec_(sourcecode, globals_new, locals_)
      if verbose:
          print(ut.color_text(sourcecode, 'python'))
      six.exec_(sourcecode, globals_new)
      # Draw intermediate steps
      if keys is None:
          #return locals_
          # Remove keys created in function execution
          ut.delete_keys(globals_new, orig_globals.keys())
          if update:
              # update input globals?
              globals_.update(globals_new)
          # ~~ TODO autodetermine the keys from the function vars
          return globals_new
      else:
          if update:
              # update input globals?
              globals_.update(globals_new)
          #var_list = ut.dict_take(locals_, keys)
          var_list = ut.dict_take(globals_new, keys)
          return var_list