docs: def get_func_sourcecode(func, stripdef=False, stripret=False,
                          strip_docstr=False, strip_comments=False,
                          remove_linenums=None):
      """
      wrapper around inspect.getsource but takes into account utool decorators
      strip flags are very hacky as of now
  
      Args:
          func (function):
          stripdef (bool):
          stripret (bool): (default = False)
          strip_docstr (bool): (default = False)
          strip_comments (bool): (default = False)
          remove_linenums (None): (default = None)
  
      CommandLine:
          python -m utool.util_inspect --test-get_func_sourcecode
  
      Example:
          >>> # DISABLE_DOCTEST
          >>> from utool.util_inspect import *  # NOQA
          >>> # build test data
          >>> func = get_func_sourcecode
          >>> stripdef = True
          >>> stripret = True
          >>> sourcecode = get_func_sourcecode(func, stripdef)
          >>> # verify results
          >>> print(result)
      """
      import utool as ut
      #try:
      inspect.linecache.clearcache()  # HACK: fix inspect bug
      sourcefile = inspect.getsourcefile(func)
      #except IOError:
      #    sourcefile = None
      if hasattr(func, '_utinfo'):
          #if 'src' in func._utinfo:
          #    sourcecode = func._utinfo['src']
          #else:
          func2 = func._utinfo['orig_func']
          sourcecode = get_func_sourcecode(func2)
      elif sourcefile is not None and (sourcefile != '<string>'):
          try_limit = 2
          for num_tries in range(try_limit):
              try:
                  #print(func)
                  sourcecode = inspect.getsource(func)
                  if not isinstance(sourcecode, six.text_type):
                      sourcecode = sourcecode.decode('utf-8')
                  #print(sourcecode)
              except (IndexError, OSError, SyntaxError) as ex:
                  ut.printex(ex, 'Error getting source',
                             keys=['sourcefile', 'func'])
                  if False:
                      # VERY HACK: try to reload the module and get a redefined
                      # version of the function
                      import imp
                      modname = get_func_modname(func)
                      funcname = ut.get_funcname(func)
                      module = sys.modules[modname]
                      # TODO: ut.reload_module()
                      module = imp.reload(module)
                      func = module.__dict__[funcname]
                  else:
                      # Fix inspect bug in python2.7
                      inspect.linecache.clearcache()
                  if num_tries + 1 != try_limit:
                      tries_left = try_limit - num_tries - 1
                      print('Attempting %d more time(s)' % (tries_left))
                  else:
                      raise
      else:
          sourcecode = None
      #orig_source = sourcecode
      #print(orig_source)
      if stripdef:
          # hacky
          sourcecode = ut.unindent(sourcecode)
          #sourcecode = ut.unindent(ut.regex_replace('def [^)]*\\):\n', '', sourcecode))
          #nodef_source = ut.regex_replace('def [^:]*\\):\n', '', sourcecode)
          regex_decor = '^@.' + ut.REGEX_NONGREEDY
          regex_defline = '^def [^:]*\\):\n'
          patern = '(' + regex_decor + ')?' + regex_defline
          nodef_source = ut.regex_replace(patern, '', sourcecode)
          sourcecode = ut.unindent(nodef_source)
          #print(sourcecode)
          pass
      if stripret:
          r""" \s is a whitespace char """
          return_ = ut.named_field('return', 'return .*$')
          prereturn = ut.named_field('prereturn', r'^\s*')
          return_bref = ut.bref_field('return')
          prereturn_bref = ut.bref_field('prereturn')
          regex = prereturn + return_
          repl = prereturn_bref + 'pass  # ' + return_bref
          #import re
          #print(re.search(regex, sourcecode, flags=re.MULTILINE ))
          #print(re.search('return', sourcecode, flags=re.MULTILINE | re.DOTALL ))
          #print(re.search(regex, sourcecode))
          sourcecode_ = re.sub(regex, repl, sourcecode, flags=re.MULTILINE)
          #print(sourcecode_)
          sourcecode = sourcecode_
          pass
      if strip_docstr or strip_comments:
          # pip install pyminifier
          # References: http://code.activestate.com/recipes/576704/
          #from pyminifier import minification, token_utils
          def remove_docstrings_or_comments(source):
              """
              TODO: commit clean version to pyminifier
              """
              import tokenize
              from six.moves import StringIO
              io_obj = StringIO(source)
              out = ''
              prev_toktype = tokenize.INDENT
              last_lineno = -1
              last_col = 0
              for tok in tokenize.generate_tokens(io_obj.readline):
                  token_type = tok[0]
                  token_string = tok[1]
                  start_line, start_col = tok[2]
                  end_line, end_col = tok[3]
                  if start_line > last_lineno:
                      last_col = 0
                  if start_col > last_col:
                      out += (' ' * (start_col - last_col))
                  # Remove comments:
                  if strip_comments and token_type == tokenize.COMMENT:
                      pass
                  elif strip_docstr and token_type == tokenize.STRING:
                      if prev_toktype != tokenize.INDENT:
                          # This is likely a docstring; double-check we're not inside an operator:
                          if prev_toktype != tokenize.NEWLINE:
                              if start_col > 0:
                                  out += token_string
                  else:
                      out += token_string
                  prev_toktype = token_type
                  last_col = end_col
                  last_lineno = end_line
              return out
          sourcecode = remove_docstrings_or_comments(sourcecode)
          #sourcecode = minification.remove_comments_and_docstrings(sourcecode)
          #tokens = token_utils.listified_tokenizer(sourcecode)
          #minification.remove_comments(tokens)
          #minification.remove_docstrings(tokens)
          #token_utils.untokenize(tokens)
  
      if remove_linenums is not None:
          source_lines = sourcecode.strip('\n').split('\n')
          ut.delete_items_by_index(source_lines, remove_linenums)
          sourcecode = '\n'.join(source_lines)
      return sourcecode