import sys
import difflib
import subprocess
import re
import pydoc
import unittest
import test.test_support
from test import pydoc_mod
expected_text_pattern = \
"""
NAME
    test.pydoc_mod - This is a test module for test_pydoc
FILE
    %s
CLASSES
    __builtin__.object
        B
    A
\x20\x20\x20\x20
    class A
     |  Hello and goodbye
     |\x20\x20
     |  Methods defined here:
     |\x20\x20
     |  __init__()
     |      Wow, I have no function!
\x20\x20\x20\x20
    class B(__builtin__.object)
     |  Data descriptors defined here:
     |\x20\x20
     |  __dict__
     |      dictionary for instance variables (if defined)
     |\x20\x20
     |  __weakref__
     |      list of weak references to the object (if defined)
     |\x20\x20
     |  ----------------------------------------------------------------------
     |  Data and other attributes defined here:
     |\x20\x20
     |  NO_MEANING = 'eggs'
FUNCTIONS
    doc_func()
        This function solves all of the world's problems:
        hunger
        lack of Python
        war
\x20\x20\x20\x20
    nodoc_func()
DATA
    __author__ = 'Benjamin Peterson'
    __credits__ = 'Nobody'
    __package__ = None
    __version__ = '1.2.3.4'
VERSION
    1.2.3.4
AUTHOR
    Benjamin Peterson
CREDITS
    Nobody
""".strip()
expected_html_pattern = \
"""
    This is a test module for test_pydoc
| Classes
 | 
\x20\x20\x20\x20
|  |  | 
__builtin__.object
B
A
 
 
\x20\x20\x20\x20| class A
 |  |  | Hello and goodbye 
 |  |  | Methods defined here: 
 __init__()Wow, I have no function!
 |  
 
\x20\x20\x20\x20| class B(__builtin__.object)
 |  |  |  | Data descriptors defined here: 
 __dict__dictionary for instance variables (if defined)
 __weakref__list of weak references to the object (if defined)
 Data and other attributes defined here:
 
 NO_MEANING = 'eggs'
 |  | 
| Functions
 | 
\x20\x20\x20\x20
|  |  | doc_func()This function solves all of the world's problems:hunger
 lack of Python
 war
 nodoc_func()
 | 
| Data
 | 
\x20\x20\x20\x20
|  |  | __author__ = 'Benjamin Peterson' __credits__ = 'Nobody'
 __package__ = None
 __version__ = '1.2.3.4'
 | 
| Author
 | 
\x20\x20\x20\x20
|  |  | Benjamin Peterson | 
| Credits
 | 
\x20\x20\x20\x20
|  |  | Nobody | 
""".strip()
# output pattern for missing module
missing_pattern = "no Python documentation found for '%s'"
def run_pydoc(module_name, *args):
    """
    Runs pydoc on the specified module. Returns the stripped
    output of pydoc.
    """
    cmd = [sys.executable, pydoc.__file__, " ".join(args), module_name]
    output = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read()
    return output.strip()
def get_pydoc_html(module):
    "Returns pydoc generated output as html"
    output = pydoc.HTMLDoc().docmodule(module)
    return output.strip()
def get_pydoc_text(module):
    "Returns pydoc generated output as text"
    output = pydoc.TextDoc().docmodule(module)
    # cleanup the extra text formatting that pydoc preforms
    patt = re.compile('\b.')
    output = patt.sub('', output)
    return output.strip()
def print_diffs(text1, text2):
    "Prints unified diffs for two texts"
    lines1 = text1.splitlines()
    lines2 = text2.splitlines()
    diffs = difflib.unified_diff(lines1, lines2, n=0)
    diffs = list(diffs) # its a generator
    print '\n'.join(diffs)
def get_mod_file(module):
    """Returns the name of the .py file if the module is compiled"""
    mod_file = module.__file__
    return mod_file if mod_file.endswith("py") else mod_file[:-3] + "py"
class CLINotHereTest(unittest.TestCase):
    def test_not_here(self):
        missing_module = "test.i_am_not_here"
        result = run_pydoc(missing_module)
        expected = missing_pattern % missing_module
        self.assertEqual(expected, result,
            "documentation for missing module found")
class PyDocDocTest(unittest.TestCase):
    def test_html_doc(self):
        result = get_pydoc_html(pydoc_mod)
        mod_file = get_mod_file(pydoc_mod)
        expected_html = expected_html_pattern % (mod_file, mod_file)
        if result != expected_html:
            print_diffs(result, expected_html)
            self.fail("outputs are not equal, see diff above")
    def test_text_doc(self):
        result = get_pydoc_text(pydoc_mod)
        expected_text = expected_text_pattern % get_mod_file(pydoc_mod)
        if result != expected_text:
            print_diffs(result, expected_text)
            self.fail("outputs are not equal, see diff above")
class TestDescriptions(unittest.TestCase):
    def test_module(self):
        # Check that pydocfodder module can be described
        from test import pydocfodder
        doc = pydoc.render_doc(pydocfodder)
        assert "pydocfodder" in doc
    def test_classic_class(self):
        class C: "Classic class"
        c = C()
        self.failUnlessEqual(pydoc.describe(C), 'class C')
        self.failUnlessEqual(pydoc.describe(c), 'instance of C')
        self.failUnless('instance of C in module test.test_pydoc'
                        in pydoc.render_doc(c))
    def test_class(self):
        class C(object): "New-style class"
        c = C()
        self.failUnlessEqual(pydoc.describe(C), 'class C')
        self.failUnlessEqual(pydoc.describe(c), 'C')
        self.failUnless('C in module test.test_pydoc object'
                        in pydoc.render_doc(c))
def test_main():
    test.test_support.run_unittest(CLINotHereTest,
                                   PyDocDocTest,
                                   TestDescriptions)
if __name__ == "__main__":
    test_main()