docs: def profile(fn=None, skip=0, filename=None, immediate=False, dirs=False,
              sort=None, entries=40,
              profiler=('cProfile', 'profile', 'hotshot')):
      """Mark `fn` for profiling.
  
      If `skip` is > 0, first `skip` calls to `fn` will not be profiled.
  
      If `immediate` is False, profiling results will be printed to
      sys.stdout on program termination.  Otherwise results will be printed
      after each call.
  
      If `dirs` is False only the name of the file will be printed.
      Otherwise the full path is used.
  
      `sort` can be a list of sort keys (defaulting to ['cumulative',
      'time', 'calls']).  The following ones are recognized::
  
          'calls'      -- call count
          'cumulative' -- cumulative time
          'file'       -- file name
          'line'       -- line number
          'module'     -- file name
          'name'       -- function name
          'nfl'        -- name/file/line
          'pcalls'     -- call count
          'stdname'    -- standard name
          'time'       -- internal time
  
      `entries` limits the output to the first N entries.
  
      `profiler` can be used to select the preferred profiler, or specify a
      sequence of them, in order of preference.  The default is ('cProfile'.
      'profile', 'hotshot').
  
      If `filename` is specified, the profile stats will be stored in the
      named file.  You can load them pstats.Stats(filename).
  
      Usage::
  
          def fn(...):
              ...
          fn = profile(fn, skip=1)
  
      If you are using Python 2.4, you should be able to use the decorator
      syntax::
  
          @profile(skip=3)
          def fn(...):
              ...
  
      or just ::
  
          @profile
          def fn(...):
              ...
  
      """
      if fn is None: # @profile() syntax -- we are a decorator maker
          def decorator(fn):
              return profile(fn, skip=skip, filename=filename,
                             immediate=immediate, dirs=dirs,
                             sort=sort, entries=entries,
                             profiler=profiler)
          return decorator
      # @profile syntax -- we are a decorator.
      if isinstance(profiler, str):
          profiler = [profiler]
      for p in profiler:
          if p in AVAILABLE_PROFILERS:
              profiler_class = AVAILABLE_PROFILERS[p]
              break
      else:
          raise ValueError('only these profilers are available: %s'
                               % ', '.join(AVAILABLE_PROFILERS))
      fp = profiler_class(fn, skip=skip, filename=filename,
                          immediate=immediate, dirs=dirs,
                          sort=sort, entries=entries)
      # fp = HotShotFuncProfile(fn, skip=skip, filename=filename, ...)
           # or HotShotFuncProfile
      # We cannot return fp or fp.__call__ directly as that would break method
      # definitions, instead we need to return a plain function.
      def new_fn(*args, **kw):
          return fp(*args, **kw)
      new_fn.__doc__ = fn.__doc__
      new_fn.__name__ = fn.__name__
      new_fn.__dict__ = fn.__dict__
      new_fn.__module__ = fn.__module__
      return new_fn