3

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.

MattV
  • 1,353
  • 18
  • 42

3 Answers3

3

I have written a short function that takes a string containing the module name that creates a html file using pydoc and shows in your default browser.

It does not set up a web server, instead it just loads the file into a web browser.

import subprocess
import os
import tempfile
import sys
import time

def show_help(module):
    """ Create and show HTML page of module documentation 

        Pass module as a string
    """

    # Create temporary directory to store documentation in
    with tempfile.TemporaryDirectory() as temp_folder:
        orignal_cwd = os.getcwd()

        # Change working directory to temporary folder
        os.chdir(temp_folder)

        # Create HTML page of doc
        subprocess.call(["pydoc", "-w", module], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)       

        filepath = os.path.join(temp_folder, module + ".html")

        # Reset working directory
        os.chdir(orignal_cwd)

        # Open default program to view HTML files
        if sys.platform.startswith('darwin'):
            subprocess.call(('open', filepath), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        elif os.name == 'nt':
            os.startfile(filepath)
        elif os.name == 'posix':
            subprocess.call(('xdg-open', filepath), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

        # Sleep for 2 seconds so browser can open before deleting file
        time.sleep(2)

I have tested it with python3.4 on linux where it works fine but I have not checked it with Windows.

You can import it as a module in the interpreter and then just use show_help("module name") to view the documentation where "module name" is the module name as a string. Such as show_help("os").

mpursuit
  • 663
  • 6
  • 22
  • Unfortunately it's not working on Python 2.7 on Windows 8 (tempfile does not have TemporaryDirectory? Sounds like a nice script though! Thanks. Perhaps a script to copy the help(x) output to clipboard would suffice here, but I'm not entirely sure how that'd work either! – MattV Dec 21 '14 at 21:54
  • You can find TemporaryDirectory code here: http://stackoverflow.com/a/19299884/1869821 Also, you can put code in pystartup file and export PYTHONSTARTUP=/home/user/.pystartup (Probably use set command in autoexec.bat, I forget dos). Rename the function to replace builtin help and it will be there every time you start the interpreter. – umeboshi Dec 26 '14 at 21:29
  • Unfortunately it's still not working! Getting an AttributeError: 'module' object has no attribute 'DEVNULL'. Tried to alter it in a few ways, but couldn't get it to work. Any ideas? – MattV Dec 28 '14 at 16:50
  • @MVersteeg you could replace call with `subprocess.check_output([...])`. Without assigning the return value to anything it will just disappear. Looks like you'll have to do that a couple of times, though. – Wayne Werner Dec 30 '14 at 00:35
1

You can use pydoc.render_doc to create an html file .

import pydoc
str_help = pydoc.render_doc(str)
with open("str_doc.html", "w") as doc_file:
    doc_file.write(str_help)

Then you could open this file in a web-browser :

import webbrowser
webbrowser.open("file:///path/to/your/doc_file.html")
Alexander
  • 12,424
  • 5
  • 59
  • 76
0

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)
MattV
  • 1,353
  • 18
  • 42
  • One question though, I see that pydoc.help(a) properly works when doing 'test = re.search("htt", "http://"), pydoc.help(a)'. It doesn't work, however, when building the html page. Any ideas on why? pydoc.writedoc(a) doesn't work in this scenario, but the pydoc line 'page = html.page(describe(object), html.document(object, name))' doesn't return the same text output either. – MattV Dec 31 '14 at 15:00