Search code examples
pythonpydoc

Producing a single HTML help page for a module, function, class similar to R


I'm looking for a way to ease my way into new modules by extensively using the help().

The issue I'm currently having is that the output is very helpful, but difficult to scroll through in the interactive interpreter. What I'm looking for is a way for me to explore python modules in a way that is similar to how R handles documentation (when using R PRoject).

R outputs a single HTML file that allows you to scroll through the parameters, functions; I'm using python 2.7 on Windows.

I found pydoc, which outputs exactly what I'm looking for in the console, but I'm not entirely sure how I'd go about moving away from the 'webserver' documenting all of my installed packages to just serving and opening a single html page once I type help(x), outputting what it would normally output in the interpreter.

Any help is greatly appreciated.


Solution

  • Thanks for the answers. My final solution is a combination of your two methods:

    • I imported the TemporaryDirectory() as mentioned above;
    • I then call pydoc.writedoc(x) to generate an html file in the temp. folder;
    • I used a different snippet to suppress printing 'wrote x.html' from pydoc;
    • I used webbrowser.open_new(url) to open a new page in my default browser.

    Calling help(os) now generates a temporary HTML file (in pydoc) and opens it in my standard browser; this was exactly what I needed.

    from __future__ import print_function
    
    import pydoc
    import os
    import sys
    import time
    import webbrowser
    
    import warnings as _warnings
    import os as _os
    
    from tempfile import mkdtemp
    from contextlib import contextmanager
    
    
    @contextmanager
    def suppress_stdout():
        """ Suppresses output of all functions called as follows:
        with suppress_stdout():
            functioncall()
        source: http://thesmithfam.org/blog/2012/10/25/temporarily-suppress-console-output-in-python/
        """
        with open(os.devnull, "w") as devnull:
            old_stdout = sys.stdout
            sys.stdout = devnull
            try:
                yield
            finally:
                sys.stdout = old_stdout
    
    
    class TemporaryDirectory(object):
        """Create and return a temporary directory.  This has the same
        behavior as mkdtemp but can be used as a context manager.  For
        example:
    
            with TemporaryDirectory() as tmpdir:
                ...
    
        Upon exiting the context, the directory and everything contained
        in it are removed.
        """
    
        def __init__(self, suffix="", prefix="tmp", dir=None):
            self._closed = False
            self.name = None # Handle mkdtemp raising an exception
            self.name = mkdtemp(suffix, prefix, dir)
    
        def __repr__(self):
            return "<{} {!r}>".format(self.__class__.__name__, self.name)
    
        def __enter__(self):
            return self.name
    
        def cleanup(self, _warn=False):
            if self.name and not self._closed:
                try:
                    self._rmtree(self.name)
                except (TypeError, AttributeError) as ex:
                    # Issue #10188: Emit a warning on stderr
                    # if the directory could not be cleaned
                    # up due to missing globals
                    if "None" not in str(ex):
                        raise
                    print("ERROR: {!r} while cleaning up {!r}".format(ex, self,),
                          file=_sys.stderr)
                    return
                self._closed = True
                if _warn:
                    self._warn("Implicitly cleaning up {!r}".format(self),
                               ResourceWarning)
    
        def __exit__(self, exc, value, tb):
            self.cleanup()
    
        def __del__(self):
            # Issue a ResourceWarning if implicit cleanup needed
            self.cleanup(_warn=True)
    
        # XXX (ncoghlan): The following code attempts to make
        # this class tolerant of the module nulling out process
        # that happens during CPython interpreter shutdown
        # Alas, it doesn't actually manage it. See issue #10188
        _listdir = staticmethod(_os.listdir)
        _path_join = staticmethod(_os.path.join)
        _isdir = staticmethod(_os.path.isdir)
        _islink = staticmethod(_os.path.islink)
        _remove = staticmethod(_os.remove)
        _rmdir = staticmethod(_os.rmdir)
        _warn = _warnings.warn
    
        def _rmtree(self, path):
            # Essentially a stripped down version of shutil.rmtree.  We can't
            # use globals because they may be None'ed out at shutdown.
            for name in self._listdir(path):
                fullname = self._path_join(path, name)
                try:
                    isdir = self._isdir(fullname) and not self._islink(fullname)
                except OSError:
                    isdir = False
                if isdir:
                    self._rmtree(fullname)
                else:
                    try:
                        self._remove(fullname)
                    except OSError:
                        pass
            try:
                self._rmdir(path)
            except OSError:
                pass
    
    
    def help(thing):
        """ Create and show HTML page of module documentation,
        It should accept anything that the regular help() accepts.
        """
    
        # Create temporary directory to store documentation in
        with TemporaryDirectory() as temp_folder:
            orignal_cwd = os.getcwd()
    
            # Change working directory to temporary folder
            os.chdir(temp_folder)
    
            # Create HTML page of doc
            object, name = pydoc.resolve(thing)
            pydoc.writedoc(thing)
            with suppress_stdout():
                filepath = os.path.join(temp_folder, name + ".html")
    
            # Reset working directory
            os.chdir(orignal_cwd)
    
            # Open default program to view HTML files
            webbrowser.open_new(filepath)
    
            # Sleep for 2 seconds so browser can open before deleting file
            time.sleep(2)
    
    if __name__ == "__main__":
        help(os)