Subversion Repositories Scribus

Rev

Rev 1062 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1062 subik 1
#!/usr/bin/env python
2
# -*- coding: Utf8 -*-
1063 subik 3
"""Generate Python documentation in clean and nice HTML for Scribus.
1062 subik 4
Based on the PyDoc package.
5
"""
6
__author__ = "Petr Vanek <petr@yarpen.cz>"
7
__date__ = "2004/12/27"
8
__version__ = "$Id: scridoc.py 1063 2005-01-02 20:26:25Z subik $"
9
__credits__ = "Ka-Ping Yee <ping@lfw.org> for PyDoc"
10
 
11
import sys, imp, os, re, types, inspect, __builtin__
12
from repr import Repr
13
from string import expandtabs, find, join, lower, split, strip, rfind, rstrip
14
 
15
# --------------------------------------------------------- common routines
16
def pathdirs():
1063 subik 17
        """Convert sys.path into a list of absolute, existing, unique paths."""
18
        dirs = []
19
        normdirs = []
20
        for dir in sys.path:
21
                dir = os.path.abspath(dir or '.')
22
                normdir = os.path.normcase(dir)
23
                if normdir not in normdirs and os.path.isdir(dir):
24
                        dirs.append(dir)
25
                        normdirs.append(normdir)
26
        return dirs
1062 subik 27
 
28
def getdoc(object):
1063 subik 29
        """Get the doc string or comments for an object."""
30
        result = inspect.getdoc(object) or inspect.getcomments(object)
31
        return result and re.sub('^ *\n', '', rstrip(result)) or ''
1062 subik 32
 
33
def splitdoc(doc):
1063 subik 34
        """Split a doc string into a synopsis line (if any) and the rest."""
35
        lines = split(strip(doc), '\n')
36
        if len(lines) == 1:
37
                return lines[0], ''
38
        elif len(lines) >= 2 and not rstrip(lines[1]):
39
                return lines[0], join(lines[2:], '\n')
40
        return '', join(lines, '\n')
1062 subik 41
 
42
def classname(object, modname):
1063 subik 43
        """Get a class name and qualify it with a module name if necessary."""
44
        name = object.__name__
45
        if object.__module__ != modname:
46
                name = object.__module__ + '.' + name
47
        return name
1062 subik 48
 
49
def isdata(object):
1063 subik 50
        """Check if an object is of a type that probably means it's data."""
51
        return not (inspect.ismodule(object) or inspect.isclass(object) or
52
                                inspect.isroutine(object) or inspect.isframe(object) or
53
                                inspect.istraceback(object) or inspect.iscode(object))
1062 subik 54
 
55
def replace(text, *pairs):
1063 subik 56
        """Do a series of global replacements on a string."""
57
        while pairs:
58
                text = join(split(text, pairs[0]), pairs[1])
59
                pairs = pairs[2:]
60
        return text
1062 subik 61
 
62
def cram(text, maxlen):
1063 subik 63
        """Omit part of a string if needed to make it fit in a maximum length."""
64
        if len(text) > maxlen:
65
                pre = max(0, (maxlen-3)//2)
66
                post = max(0, maxlen-3-pre)
67
                return text[:pre] + '...' + text[len(text)-post:]
68
        return text
1062 subik 69
 
70
def stripid(text):
1063 subik 71
        """Remove the hexadecimal id from a Python object representation."""
72
        # The behaviour of %p is implementation-dependent; we check two cases.
73
        for pattern in [' at 0x[0-9a-f]{6,}(>+)$', ' at [0-9A-F]{8,}(>+)$']:
74
                if re.search(pattern, repr(Exception)):
75
                        return re.sub(pattern, '\\1', text)
76
        return text
1062 subik 77
 
78
def _is_some_method(object):
1063 subik 79
        return inspect.ismethod(object) or inspect.ismethoddescriptor(object)
1062 subik 80
 
81
def allmethods(cl):
1063 subik 82
        methods = {}
83
        for key, value in inspect.getmembers(cl, _is_some_method):
84
                methods[key] = 1
85
        for base in cl.__bases__:
86
                methods.update(allmethods(base)) # all your base are belong to us
87
        for key in methods.keys():
88
                methods[key] = getattr(cl, key)
89
        return methods
1062 subik 90
 
91
def _split_list(s, predicate):
1063 subik 92
        """Split sequence s via predicate, and return pair ([true], [false]).
1062 subik 93
 
1063 subik 94
        The return value is a 2-tuple of lists,
95
                ([x for x in s if predicate(x)],
96
                 [x for x in s if not predicate(x)])
97
        """
98
        yes = []
99
        no = []
100
        for x in s:
101
                if predicate(x):
102
                        yes.append(x)
103
                else:
104
                        no.append(x)
105
        return yes, no
1062 subik 106
 
107
def visiblename(name):
1063 subik 108
        """Decide whether to show documentation on a variable."""
109
        # Certain special names are redundant.
110
        if name in ['__builtins__', '__doc__', '__file__', '__path__',
111
                                '__module__', '__name__']: return 0
112
        # Private names are hidden, but special names are displayed.
113
        if name.startswith('__') and name.endswith('__'): return 1
114
        return not name.startswith('_')
1062 subik 115
 
116
# ----------------------------------------------------- module manipulation
117
 
118
def ispackage(path):
1063 subik 119
        """Guess whether a path refers to a package directory."""
120
        if os.path.isdir(path):
121
                for ext in ['.py', '.pyc', '.pyo']:
122
                        if os.path.isfile(os.path.join(path, '__init__' + ext)):
123
                                return True
124
        return False
1062 subik 125
 
126
def synopsis(filename, cache={}):
1063 subik 127
        """Get the one-line summary out of a module file."""
128
        mtime = os.stat(filename).st_mtime
129
        lastupdate, result = cache.get(filename, (0, None))
130
        if lastupdate < mtime:
131
                info = inspect.getmoduleinfo(filename)
132
                file = open(filename)
133
                if info and 'b' in info[2]: # binary modules have to be imported
134
                        try: module = imp.load_module('__temp__', file, filename, info[1:])
135
                        except: return None
136
                        result = split(module.__doc__ or '', '\n')[0]
137
                        del sys.modules['__temp__']
138
                else: # text modules can be directly examined
139
                        line = file.readline()
140
                        while line[:1] == '#' or not strip(line):
141
                                line = file.readline()
142
                                if not line: break
143
                        line = strip(line)
144
                        if line[:4] == 'r"""': line = line[1:]
145
                        if line[:3] == '"""':
146
                                line = line[3:]
147
                                if line[-1:] == '\\': line = line[:-1]
148
                                while not strip(line):
149
                                        line = file.readline()
150
                                        if not line: break
151
                                result = strip(split(line, '"""')[0])
152
                        else: result = None
153
                file.close()
154
                cache[filename] = (mtime, result)
155
        return result
1062 subik 156
 
157
class ErrorDuringImport(Exception):
1063 subik 158
        """Errors that occurred while trying to import something to document it."""
159
        def __init__(self, filename, (exc, value, tb)):
160
                self.filename = filename
161
                self.exc = exc
162
                self.value = value
163
                self.tb = tb
1062 subik 164
 
1063 subik 165
        def __str__(self):
166
                exc = self.exc
167
                if type(exc) is types.ClassType:
168
                        exc = exc.__name__
169
                return 'problem in %s - %s: %s' % (self.filename, exc, self.value)
1062 subik 170
 
171
def importfile(path):
1063 subik 172
        """Import a Python source file or compiled file given its path."""
173
        magic = imp.get_magic()
174
        file = open(path, 'r')
175
        if file.read(len(magic)) == magic:
176
                kind = imp.PY_COMPILED
177
        else:
178
                kind = imp.PY_SOURCE
179
        file.close()
180
        filename = os.path.basename(path)
181
        name, ext = os.path.splitext(filename)
182
        file = open(path, 'r')
183
        try:
184
                module = imp.load_module(name, file, path, (ext, 'r', kind))
185
        except:
186
                raise ErrorDuringImport(path, sys.exc_info())
187
        file.close()
188
        return module
1062 subik 189
 
190
def safeimport(path, forceload=0, cache={}):
1063 subik 191
        """Import a module; handle errors; return None if the module isn't found.
1062 subik 192
 
1063 subik 193
        If the module *is* found but an exception occurs, it's wrapped in an
194
        ErrorDuringImport exception and reraised.  Unlike __import__, if a
195
        package path is specified, the module at the end of the path is returned,
196
        not the package at the beginning.  If the optional 'forceload' argument
197
        is 1, we reload the module from disk (unless it's a dynamic extension)."""
198
        if forceload and path in sys.modules:
199
                # This is the only way to be sure.  Checking the mtime of the file
200
                # isn't good enough (e.g. what if the module contains a class that
201
                # inherits from another module that has changed?).
202
                if path not in sys.builtin_module_names:
203
                        # Python never loads a dynamic extension a second time from the
204
                        # same path, even if the file is changed or missing.  Deleting
205
                        # the entry in sys.modules doesn't help for dynamic extensions,
206
                        # so we're not even going to try to keep them up to date.
207
                        info = inspect.getmoduleinfo(sys.modules[path].__file__)
208
                        if info[3] != imp.C_EXTENSION:
209
                                cache[path] = sys.modules[path] # prevent module from clearing
210
                                del sys.modules[path]
211
        try:
212
                module = __import__(path)
213
        except:
214
                # Did the error occur before or after the module was found?
215
                (exc, value, tb) = info = sys.exc_info()
216
                if path in sys.modules:
217
                        # An error occured while executing the imported module.
218
                        raise ErrorDuringImport(sys.modules[path].__file__, info)
219
                elif exc is SyntaxError:
220
                        # A SyntaxError occurred before we could execute the module.
221
                        raise ErrorDuringImport(value.filename, info)
222
                elif exc is ImportError and \
223
                         split(lower(str(value)))[:2] == ['no', 'module']:
224
                        # The module was not found.
225
                        return None
226
                else:
227
                        # Some other error occurred during the importing process.
228
                        raise ErrorDuringImport(path, sys.exc_info())
229
        for part in split(path, '.')[1:]:
230
                try: module = getattr(module, part)
231
                except AttributeError: return None
232
        return module
1062 subik 233
 
234
# ---------------------------------------------------- formatter base class
235
 
236
class Doc:
1063 subik 237
        def document(self, object, name=None, *args):
238
                """Generate documentation for an object."""
239
                args = (object, name) + args
240
                # 'try' clause is to attempt to handle the possibility that inspect
241
                # identifies something in a way that pydoc itself has issues handling;
242
                # think 'super' and how it is a descriptor (which raises the exception
243
                # by lacking a __name__ attribute) and an instance.
244
                try:
245
                        if inspect.ismodule(object): return self.docmodule(*args)
246
                        if inspect.isclass(object): return self.docclass(*args)
247
                        if inspect.isroutine(object): return self.docroutine(*args)
248
                except AttributeError:
249
                        pass
250
                return self.docother(*args)
1062 subik 251
 
1063 subik 252
        def fail(self, object, name=None, *args):
253
                """Raise an exception for unimplemented types."""
254
                message = "don't know how to document object%s of type %s" % (
255
                        name and ' ' + repr(name), type(object).__name__)
256
                raise TypeError, message
1062 subik 257
 
1063 subik 258
        docmodule = docclass = docroutine = docother = fail
1062 subik 259
 
260
# -------------------------------------------- HTML documentation generator
261
 
262
class HTMLRepr(Repr):
1063 subik 263
        """Class for safely making an HTML representation of a Python object."""
264
        def __init__(self):
265
                Repr.__init__(self)
266
                self.maxlist = self.maxtuple = 20
267
                self.maxdict = 10
268
                self.maxstring = self.maxother = 100
1062 subik 269
 
1063 subik 270
        def escape(self, text):
271
                return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')
1062 subik 272
 
1063 subik 273
        def repr(self, object):
274
                return Repr.repr(self, object)
1062 subik 275
 
1063 subik 276
        def repr1(self, x, level):
277
                if hasattr(type(x), '__name__'):
278
                        methodname = 'repr_' + join(split(type(x).__name__), '_')
279
                        if hasattr(self, methodname):
280
                                return getattr(self, methodname)(x, level)
281
                return self.escape(cram(stripid(repr(x)), self.maxother))
1062 subik 282
 
1063 subik 283
        def repr_string(self, x, level):
284
                test = cram(x, self.maxstring)
285
                testrepr = repr(test)
286
                if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
287
                        # Backslashes are only literal in the string and are never
288
                        # needed to make any special characters, so show a raw string.
289
                        return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
290
                return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
291
                                          r'<font color="#c040c0">\1</font>',
292
                                          self.escape(testrepr))
1062 subik 293
 
1063 subik 294
        repr_str = repr_string
1062 subik 295
 
1063 subik 296
        def repr_instance(self, x, level):
297
                try:
298
                        return self.escape(cram(stripid(repr(x)), self.maxstring))
299
                except:
300
                        return self.escape('<%s instance>' % x.__class__.__name__)
1062 subik 301
 
1063 subik 302
        repr_unicode = repr_string
1062 subik 303
 
304
class HTMLDoc(Doc):
1063 subik 305
        """Formatter class for HTML documentation."""
1062 subik 306
 
1063 subik 307
        # ------------------------------------------- HTML formatting utilities
1062 subik 308
 
1063 subik 309
        _repr_instance = HTMLRepr()
310
        repr = _repr_instance.repr
311
        escape = _repr_instance.escape
1062 subik 312
 
1063 subik 313
        def page(self, title, contents):
314
                """Format an HTML page."""
315
                return '''
1062 subik 316
<qt>
317
%s
318
</qt>''' % (contents)
319
 
1063 subik 320
        def heading(self, title, fgcol, bgcol, extras=''):
321
                """Format a page heading."""
322
                return '''
1062 subik 323
<h2>%s</h2>
324
<p>debug_Extras: %s</p>
1063 subik 325
        ''' % (title, extras or '&nbsp;')
1062 subik 326
 
1063 subik 327
        def section(self, title, fgcol, bgcol, contents, width=6,
328
                                prelude='', marginalia=None, gap=' '):
329
                """Format a section with a heading."""
330
                if marginalia is None:
331
#marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
332
                        pass
333
                result = '''
1062 subik 334
<h4>debug_Section: "%s"</h4>
335
''' % (title)
1063 subik 336
                if prelude:
337
                        result = result + '''
1062 subik 338
<p>debug_Prelude: %s</p>
1063 subik 339
''' % (prelude)
340
                return result + '''
1062 subik 341
<p>debug_Contents: %s</p>
342
''' % contents
343
 
1063 subik 344
        def bigsection(self, title, *args):
345
                """Format a section with a big heading."""
346
                title = '''debug_Title<h7>%s</h7>''' % title
347
                return self.section(title, *args)
1062 subik 348
 
1063 subik 349
        def preformat(self, text):
350
                """Format literal preformatted text."""
351
                text = self.escape(expandtabs(text))
352
                return replace(text, '\n\n', '\n \n', '\n\n', '\n \n')
353
                                                #'\n', '<br>\n')
1062 subik 354
 
1063 subik 355
        def multicolumn(self, list, format, cols=4):
356
                """Format a list of items into a multi-column list."""
357
                """result = ''
358
                rows = (len(list)+cols-1)/cols
359
                for col in range(cols):
360
#result = result + '<td width="%d%%" valign=top>' % (100/cols)
361
                        for i in range(rows*col, rows*col+rows):
362
                                if i < len(list):
363
                                        result = result + format(list[i]) + ' '
364
                        result = result + '</td>'
365
                return '<p>%s</p>' % result"""
366
                pass
1062 subik 367
 
1063 subik 368
        def grey(self, text): return '<p>debug_Grey: %s</p>' % text
1062 subik 369
 
1063 subik 370
        def namelink(self, name, *dicts):
371
                """Make a link for an identifier, given name-to-URL mappings."""
372
                for dict in dicts:
373
                        if name in dict:
374
                                return '<a href="%s">%s</a>' % (dict[name], name)
375
                return name
1062 subik 376
 
1063 subik 377
        def classlink(self, object, modname):
378
                """Make a link for a class."""
379
                name, module = object.__name__, sys.modules.get(object.__module__)
380
                if hasattr(module, name) and getattr(module, name) is object:
381
                        return '<a href="%s.html#%s">%s</a>' % (
382
                                module.__name__, name, classname(object, modname))
383
                return classname(object, modname)
1062 subik 384
 
1063 subik 385
        def modulelink(self, object):
386
                """Make a link for a module."""
387
                return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)
1062 subik 388
 
1063 subik 389
        def modpkglink(self, (name, path, ispackage, shadowed)):
390
                """Make a link for a module or package to display in an index."""
391
                if shadowed:
392
                        return self.grey(name)
393
                if path:
394
                        url = '%s.%s.html' % (path, name)
395
                else:
396
                        url = '%s.html' % name
397
                if ispackage:
398
                        text = '<h7>debug_IsPackage: %s (package)</h7>' % name
399
                else:
400
                        text = name
401
                return '<a href="%s">%s</a>' % (url, text)
1062 subik 402
 
1063 subik 403
        def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
404
                """Mark up some plain text, given a context of symbols to look for.
405
                Each context dictionary maps object names to anchor names."""
406
                escape = escape or self.escape
407
                results = []
408
                here = 0
409
                pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
410
                                                                r'RFC[- ]?(\d+)|'
411
                                                                r'PEP[- ]?(\d+)|'
412
                                                                r'(self\.)?(\w+))')
413
                while True:
414
                        match = pattern.search(text, here)
415
                        if not match: break
416
                        start, end = match.span()
417
                        results.append(escape(text[here:start]))
1062 subik 418
 
1063 subik 419
                        all, scheme, rfc, pep, selfdot, name = match.groups()
420
                        if scheme:
421
                                url = escape(all).replace('"', '&quot;')
422
                                results.append('<a href="%s">%s</a>' % (url, url))
423
                        elif rfc:
424
                                url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
425
                                results.append('<a href="%s">%s</a>' % (url, escape(all)))
426
                        elif pep:
427
                                url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
428
                                results.append('<a href="%s">%s</a>' % (url, escape(all)))
429
                        elif text[end:end+1] == '(':
430
                                results.append(self.namelink(name, methods, funcs, classes))
431
                        elif selfdot:
432
                                results.append('self.<strong>%s</strong>' % name)
433
                        else:
434
                                results.append(self.namelink(name, classes))
435
                        here = end
436
                results.append(escape(text[here:]))
437
                return join(results, '')
1062 subik 438
 
1063 subik 439
        # ---------------------------------------------- type-specific routines
1062 subik 440
 
1063 subik 441
        def formattree(self, tree, modname, parent=None):
442
                """Produce HTML for a class tree as given by inspect.getclasstree()."""
443
                result = ''
444
                for entry in tree:
445
                        if type(entry) is type(()):
446
                                c, bases = entry
447
                                result = result + '<dt><font face="helvetica, arial">'
448
                                result = result + self.classlink(c, modname)
449
                                if bases and bases != (parent,):
450
                                        parents = []
451
                                        for base in bases:
452
                                                parents.append(self.classlink(base, modname))
453
                                        result = result + '(' + join(parents, ', ') + ')'
454
                                result = result + '\n</font></dt>'
455
                        elif type(entry) is type([]):
456
                                result = result + '<dd>\n%s</dd>\n' % self.formattree(
457
                                        entry, modname, c)
458
                return '<dl>\n%s</dl>\n' % result
1062 subik 459
 
1063 subik 460
        def docmodule(self, object, name=None, mod=None, *ignored):
461
                """Produce HTML documentation for a module object."""
462
                name = object.__name__ # ignore the passed-in name
463
                parts = split(name, '.')
464
                links = []
465
                for i in range(len(parts)-1):
466
                        links.append(
467
                                '<a href="%s.html">%s</a>' %
468
                                (join(parts[:i+1], '.'), parts[i]))
469
                linkedname = join(links + parts[-1:], '.')
470
                head = '<h5>debug_Linkedname:%s</h5>' % linkedname
471
                try:
472
                        path = inspect.getabsfile(object)
473
                        url = path
474
                        if sys.platform == 'win32':
475
                                import nturl2path
476
                                url = nturl2path.pathname2url(path)
477
                        filelink = '<a href="file:%s">%s</a>' % (url, path)
478
                except TypeError:
479
                        filelink = '(built-in)'
480
                info = []
481
                if hasattr(object, '__version__'):
482
                        version = str(object.__version__)
483
                        if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
484
                                version = strip(version[11:-1])
485
                        info.append('version %s' % self.escape(version))
486
                if hasattr(object, '__date__'):
487
                        info.append(self.escape(str(object.__date__)))
488
                if info:
489
                        head = head + ' (%s)' % join(info, ', ')
490
                result = self.heading(
491
                        head, '#ffffff', '#7799ee', '<a href=".">index</a><br>' + filelink)
1062 subik 492
 
1063 subik 493
                modules = inspect.getmembers(object, inspect.ismodule)
1062 subik 494
 
1063 subik 495
                classes, cdict = [], {}
496
                for key, value in inspect.getmembers(object, inspect.isclass):
497
                        if (inspect.getmodule(value) or object) is object:
498
                                if visiblename(key):
499
                                        classes.append((key, value))
500
                                        cdict[key] = cdict[value] = '#' + key
501
                for key, value in classes:
502
                        for base in value.__bases__:
503
                                key, modname = base.__name__, base.__module__
504
                                module = sys.modules.get(modname)
505
                                if modname != name and module and hasattr(module, key):
506
                                        if getattr(module, key) is base:
507
                                                if not key in cdict:
508
                                                        cdict[key] = cdict[base] = modname + '.html#' + key
509
                funcs, fdict = [], {}
510
                for key, value in inspect.getmembers(object, inspect.isroutine):
511
                        if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
512
                                if visiblename(key):
513
                                        funcs.append((key, value))
514
                                        fdict[key] = '#-' + key
515
                                        if inspect.isfunction(value): fdict[value] = fdict[key]
516
                data = []
517
                for key, value in inspect.getmembers(object, isdata):
518
                        if visiblename(key):
519
                                data.append((key, value))
1062 subik 520
 
1063 subik 521
                doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
522
                doc = doc and '<tt>%s</tt>' % doc
523
                result = result + '<p>%s</p>\n' % doc
1062 subik 524
 
1063 subik 525
                if hasattr(object, '__path__'):
526
                        modpkgs = []
527
                        modnames = []
528
                        for file in os.listdir(object.__path__[0]):
529
                                path = os.path.join(object.__path__[0], file)
530
                                modname = inspect.getmodulename(file)
531
                                if modname != '__init__':
532
                                        if modname and modname not in modnames:
533
                                                modpkgs.append((modname, name, 0, 0))
534
                                                modnames.append(modname)
535
                                        elif ispackage(path):
536
                                                modpkgs.append((file, name, 1, 0))
537
                        modpkgs.sort()
538
                        contents = self.multicolumn(modpkgs, self.modpkglink)
539
                        result = result + self.bigsection(
540
                                'Package Contents', '#ffffff', '#aa55cc', contents)
541
                elif modules:
542
                        contents = self.multicolumn(
543
                                modules, lambda (key, value), s=self: s.modulelink(value))
544
                        result = result + self.bigsection(
545
                                'Modules', '#fffff', '#aa55cc', contents)
1062 subik 546
 
1063 subik 547
                if classes:
548
                        classlist = map(lambda (key, value): value, classes)
549
                        contents = [
550
                                self.formattree(inspect.getclasstree(classlist, 1), name)]
551
                        for key, value in classes:
552
                                contents.append(self.document(value, key, name, fdict, cdict))
553
                        result = result + self.bigsection(
554
                                'Classes', '#ffffff', '#ee77aa', join(contents))
555
                if funcs:
556
                        contents = []
557
                        for key, value in funcs:
558
                                contents.append(self.document(value, key, name, fdict, cdict))
559
                        result = result + self.bigsection(
560
                                'Functions', '#ffffff', '#eeaa77', join(contents))
561
                if data:
562
                        contents = []
563
                        for key, value in data:
564
                                contents.append(self.document(value, key))
565
                        result = result + self.bigsection(
566
                                'Data', '#ffffff', '#55aa55', join(contents, '<br>\n'))
567
                if hasattr(object, '__author__'):
568
                        contents = self.markup(str(object.__author__), self.preformat)
569
                        result = result + self.bigsection(
570
                                'Author', '#ffffff', '#7799ee', contents)
571
                if hasattr(object, '__credits__'):
572
                        contents = self.markup(str(object.__credits__), self.preformat)
573
                        result = result + self.bigsection(
574
                                'Credits', '#ffffff', '#7799ee', contents)
1062 subik 575
 
1063 subik 576
                return result
1062 subik 577
 
1063 subik 578
        def docclass(self, object, name=None, mod=None, funcs={}, classes={},
579
                                 *ignored):
580
                """Produce HTML documentation for a class object."""
581
                realname = object.__name__
582
                name = name or realname
583
                bases = object.__bases__
1062 subik 584
 
1063 subik 585
                contents = []
586
                push = contents.append
1062 subik 587
 
1063 subik 588
                # Cute little class to pump out a horizontal rule between sections.
589
                class HorizontalRule:
590
                        pass
591
                        """def __init__(self):
592
                                self.needone = 0
593
                        def maybe(self):
594
                                if self.needone:
595
                                        push('<hr>\n')
596
                                self.needone = 1"""
597
                hr = HorizontalRule()
1062 subik 598
 
1063 subik 599
                # List the mro, if non-trivial.
600
                mro = list(inspect.getmro(object))
601
                if len(mro) > 2:
602
                        hr.maybe()
603
                        push('<p>Method resolution order:</p>\n')
604
                        for base in mro:
605
                                push('%s<br>\n' % self.classlink(base, object.__module__))
606
                        push('\n')
1062 subik 607
 
1063 subik 608
                def spill(msg, attrs, predicate):
609
                        ok, attrs = _split_list(attrs, predicate)
610
                        if ok:
611
                                hr.maybe()
612
                                push(msg)
613
                                for name, kind, homecls, value in ok:
614
                                        push(self.document(getattr(object, name), name, mod,
615
                                                                           funcs, classes, mdict, object))
616
                                        push('\n')
617
                        return attrs
1062 subik 618
 
1063 subik 619
                def spillproperties(msg, attrs, predicate):
620
                        ok, attrs = _split_list(attrs, predicate)
621
                        if ok:
622
                                hr.maybe()
623
                                push(msg)
624
                                for name, kind, homecls, value in ok:
625
                                        push('<dl><dt><strong>%s</strong></dt>\n' % name)
626
                                        if value.__doc__ is not None:
627
                                                doc = self.markup(value.__doc__, self.preformat,
628
                                                                                  funcs, classes, mdict)
629
                                                push('<dd><tt>%s</tt></dd>\n' % doc)
630
                                        for attr, tag in [('fget', '<em>get</em>'),
631
                                                                          ('fset', '<em>set</em>'),
632
                                                                          ('fdel', '<em>delete</em>')]:
633
                                                func = getattr(value, attr)
634
                                                if func is not None:
635
                                                        base = self.document(func, tag, mod,
636
                                                                                                 funcs, classes, mdict, object)
637
                                                        push('<dd>%s</dd>\n' % base)
638
                                        push('</dl>\n')
639
                        return attrs
1062 subik 640
 
1063 subik 641
                def spilldata(msg, attrs, predicate):
642
                        ok, attrs = _split_list(attrs, predicate)
643
                        if ok:
644
                                hr.maybe()
645
                                push(msg)
646
                                for name, kind, homecls, value in ok:
647
                                        base = self.docother(getattr(object, name), name, mod)
648
                                        if callable(value) or inspect.isdatadescriptor(value):
649
                                                doc = getattr(value, "__doc__", None)
650
                                        else:
651
                                                doc = None
652
                                        if doc is None:
653
                                                push('<dl><dt>%s</dl>\n' % base)
654
                                        else:
655
                                                doc = self.markup(getdoc(value), self.preformat,
656
                                                                                  funcs, classes, mdict)
657
                                                doc = '<dd><tt>%s</tt>' % doc
658
                                                push('<dl><dt>%s%s</dl>\n' % (base, doc))
659
                                        push('\n')
660
                        return attrs
1062 subik 661
 
1063 subik 662
                attrs = filter(lambda (name, kind, cls, value): visiblename(name),
663
                                           inspect.classify_class_attrs(object))
664
                mdict = {}
665
                for key, kind, homecls, value in attrs:
666
                        mdict[key] = anchor = '#' + name + '-' + key
667
                        value = getattr(object, key)
668
                        try:
669
                                # The value may not be hashable (e.g., a data attr with
670
                                # a dict or list value).
671
                                mdict[value] = anchor
672
                        except TypeError:
673
                                pass
1062 subik 674
 
1063 subik 675
                while attrs:
676
                        if mro:
677
                                thisclass = mro.pop(0)
678
                        else:
679
                                thisclass = attrs[0][2]
680
                        attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1062 subik 681
 
1063 subik 682
                        if thisclass is __builtin__.object:
683
                                attrs = inherited
684
                                continue
685
                        elif thisclass is object:
686
                                tag = 'defined here'
687
                        else:
688
                                tag = 'inherited from %s' % self.classlink(thisclass,
689
                                                                                                                   object.__module__)
690
                        tag += ':<br>\n'
1062 subik 691
 
1063 subik 692
                        # Sort attrs by name.
693
                        attrs.sort(lambda t1, t2: cmp(t1[0], t2[0]))
1062 subik 694
 
1063 subik 695
                        # Pump out the attrs, segregated by kind.
696
                        attrs = spill('Methods %s' % tag, attrs,
697
                                                  lambda t: t[1] == 'method')
698
                        attrs = spill('Class methods %s' % tag, attrs,
699
                                                  lambda t: t[1] == 'class method')
700
                        attrs = spill('Static methods %s' % tag, attrs,
701
                                                  lambda t: t[1] == 'static method')
702
                        attrs = spillproperties('Properties %s' % tag, attrs,
703
                                                                        lambda t: t[1] == 'property')
704
                        attrs = spilldata('Data and other attributes %s' % tag, attrs,
705
                                                          lambda t: t[1] == 'data')
706
                        assert attrs == []
707
                        attrs = inherited
1062 subik 708
 
1063 subik 709
                contents = ''.join(contents)
1062 subik 710
 
1063 subik 711
                if name == realname:
712
                        title = '<a name="%s">class <code>%s</code></a>' % (
713
                                name, realname)
714
                else:
715
                        title = '<code>%s</code> = <a name="%s">class %s</a>' % (
716
                                name, name, realname)
717
                if bases:
718
                        parents = []
719
                        for base in bases:
720
                                parents.append(self.classlink(base, object.__module__))
721
                        title = title + '(%s)' % join(parents, ', ')
722
                doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
723
                doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc
1062 subik 724
 
1063 subik 725
                return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)
1062 subik 726
 
1063 subik 727
        def formatvalue(self, object):
728
                """Format an argument default value as text."""
729
                return self.grey('=' + self.repr(object))
1062 subik 730
 
1063 subik 731
        def docroutine(self, object, name=None, mod=None,
732
                                   funcs={}, classes={}, methods={}, cl=None):
733
                """Produce HTML documentation for a function or method object."""
734
                realname = object.__name__
735
                name = name or realname
736
                anchor = (cl and cl.__name__ or '') + '-' + name
737
                note = ''
738
                skipdocs = 0
739
                if inspect.ismethod(object):
740
                        imclass = object.im_class
741
                        if cl:
742
                                if imclass is not cl:
743
                                        note = ' from ' + self.classlink(imclass, mod)
744
                        else:
745
                                if object.im_self:
746
                                        note = ' method of %s instance' % self.classlink(
747
                                                object.im_self.__class__, mod)
748
                                else:
749
                                        note = ' unbound %s method' % self.classlink(imclass,mod)
750
                        object = object.im_func
1062 subik 751
 
1063 subik 752
                if name == realname:
753
                        title = '<a name="%s">%s</a>' % (anchor, realname)
754
                else:
755
                        if (cl and realname in cl.__dict__ and
756
                                cl.__dict__[realname] is object):
757
                                reallink = '<a href="#%s">%s</a>' % (
758
                                        cl.__name__ + '-' + realname, realname)
759
                                skipdocs = 1
760
                        else:
761
                                reallink = realname
762
                        title = '<a name="%s">%s</a> = %s' % (
763
                                anchor, name, reallink)
764
                if inspect.isfunction(object):
765
                        args, varargs, varkw, defaults = inspect.getargspec(object)
766
                        argspec = inspect.formatargspec(
767
                                args, varargs, varkw, defaults, formatvalue=self.formatvalue)
768
                        if realname == '<lambda>':
769
                                title = '%s <i>lambda</i> ' % name
770
                                argspec = argspec[1:-1] # remove parentheses
771
                else:
772
                        argspec = '(...)'
1062 subik 773
 
1063 subik 774
                decl = title + argspec + (note and self.grey(
775
                           '%s' % note))
1062 subik 776
 
1063 subik 777
                if skipdocs:
778
                        return '<dl><dt>%s</dt></dl>\n' % decl
779
                else:
780
                        doc = self.markup(
781
                                getdoc(object), self.preformat, funcs, classes, methods)
782
                        doc = doc and '<dd><tt>%s</tt></dd>' % doc
783
                        return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
1062 subik 784
 
1063 subik 785
        def docother(self, object, name=None, mod=None, *ignored):
786
                """Produce HTML documentation for a data object."""
787
                lhs = name and 'debug_Docother: %s = ' % name or ''
788
                return lhs + self.repr(object) + '<br>'
1062 subik 789
 
1063 subik 790
        def index(self, dir, shadowed=None):
791
                """Generate an HTML index for a directory of modules."""
792
                modpkgs = []
793
                if shadowed is None: shadowed = {}
794
                seen = {}
795
                files = os.listdir(dir)
1062 subik 796
 
1063 subik 797
                def found(name, ispackage,
798
                                  modpkgs=modpkgs, shadowed=shadowed, seen=seen):
799
                        if name not in seen:
800
                                modpkgs.append((name, '', ispackage, name in shadowed))
801
                                seen[name] = 1
802
                                shadowed[name] = 1
1062 subik 803
 
1063 subik 804
                # Package spam/__init__.py takes precedence over module spam.py.
805
                for file in files:
806
                        path = os.path.join(dir, file)
807
                        if ispackage(path): found(file, 1)
808
                for file in files:
809
                        path = os.path.join(dir, file)
810
                        if os.path.isfile(path):
811
                                modname = inspect.getmodulename(file)
812
                                if modname: found(modname, 0)
1062 subik 813
 
1063 subik 814
                modpkgs.sort()
815
                contents = self.multicolumn(modpkgs, self.modpkglink)
816
                return self.bigsection(dir, '#ffffff', '#ee77aa', contents)
1062 subik 817
 
818
# -------------------------------------------- text documentation generator
819
 
820
class TextRepr(Repr):
1063 subik 821
        """Class for safely making a text representation of a Python object."""
822
        def __init__(self):
823
                Repr.__init__(self)
824
                self.maxlist = self.maxtuple = 20
825
                self.maxdict = 10
826
                self.maxstring = self.maxother = 100
1062 subik 827
 
1063 subik 828
        def repr1(self, x, level):
829
                if hasattr(type(x), '__name__'):
830
                        methodname = 'repr_' + join(split(type(x).__name__), '_')
831
                        if hasattr(self, methodname):
832
                                return getattr(self, methodname)(x, level)
833
                return cram(stripid(repr(x)), self.maxother)
1062 subik 834
 
1063 subik 835
        def repr_string(self, x, level):
836
                test = cram(x, self.maxstring)
837
                testrepr = repr(test)
838
                if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
839
                        # Backslashes are only literal in the string and are never
840
                        # needed to make any special characters, so show a raw string.
841
                        return 'r' + testrepr[0] + test + testrepr[0]
842
                return testrepr
1062 subik 843
 
1063 subik 844
        repr_str = repr_string
1062 subik 845
 
1063 subik 846
        def repr_instance(self, x, level):
847
                try:
848
                        return cram(stripid(repr(x)), self.maxstring)
849
                except:
850
                        return '<%s instance>' % x.__class__.__name__
1062 subik 851
 
852
class TextDoc(Doc):
1063 subik 853
        """Formatter class for text documentation."""
1062 subik 854
 
1063 subik 855
        # ------------------------------------------- text formatting utilities
1062 subik 856
 
1063 subik 857
        _repr_instance = TextRepr()
858
        repr = _repr_instance.repr
1062 subik 859
 
1063 subik 860
        def bold(self, text):
861
                """Format a string in bold by overstriking."""
862
                return join(map(lambda ch: ch + '\b' + ch, text), '')
1062 subik 863
 
1063 subik 864
        def indent(self, text, prefix=' '):
865
                """Indent text by prepending a given prefix to each line."""
866
                if not text: return ''
867
                lines = split(text, '\n')
868
                lines = map(lambda line, prefix=prefix: prefix + line, lines)
869
                if lines: lines[-1] = rstrip(lines[-1])
870
                return join(lines, '\n')
1062 subik 871
 
1063 subik 872
        def section(self, title, contents):
873
                """Format a section with a given heading."""
874
                return self.bold(title) + '\n' + rstrip(self.indent(contents)) + '\n\n'
1062 subik 875
 
1063 subik 876
        # ---------------------------------------------- type-specific routines
1062 subik 877
 
1063 subik 878
        def formattree(self, tree, modname, parent=None, prefix=''):
879
                """Render in text a class tree as returned by inspect.getclasstree()."""
880
                result = ''
881
                for entry in tree:
882
                        if type(entry) is type(()):
883
                                c, bases = entry
884
                                result = result + prefix + classname(c, modname)
885
                                if bases and bases != (parent,):
886
                                        parents = map(lambda c, m=modname: classname(c, m), bases)
887
                                        result = result + '(%s)' % join(parents, ', ')
888
                                result = result + '\n'
889
                        elif type(entry) is type([]):
890
                                result = result + self.formattree(
891
                                        entry, modname, c, prefix + '   ')
892
                return result
1062 subik 893
 
1063 subik 894
        def docmodule(self, object, name=None, mod=None):
895
                """Produce text documentation for a given module object."""
896
                name = object.__name__ # ignore the passed-in name
897
                synop, desc = splitdoc(getdoc(object))
898
                result = self.section('NAME', name + (synop and ' - ' + synop))
1062 subik 899
 
1063 subik 900
                try:
901
                        file = inspect.getabsfile(object)
902
                except TypeError:
903
                        file = '(built-in)'
904
                result = result + self.section('FILE', file)
905
                if desc:
906
                        result = result + self.section('DESCRIPTION', desc)
1062 subik 907
 
1063 subik 908
                classes = []
909
                for key, value in inspect.getmembers(object, inspect.isclass):
910
                        if (inspect.getmodule(value) or object) is object:
911
                                if visiblename(key):
912
                                        classes.append((key, value))
913
                funcs = []
914
                for key, value in inspect.getmembers(object, inspect.isroutine):
915
                        if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
916
                                if visiblename(key):
917
                                        funcs.append((key, value))
918
                data = []
919
                for key, value in inspect.getmembers(object, isdata):
920
                        if visiblename(key):
921
                                data.append((key, value))
1062 subik 922
 
1063 subik 923
                if hasattr(object, '__path__'):
924
                        modpkgs = []
925
                        for file in os.listdir(object.__path__[0]):
926
                                path = os.path.join(object.__path__[0], file)
927
                                modname = inspect.getmodulename(file)
928
                                if modname != '__init__':
929
                                        if modname and modname not in modpkgs:
930
                                                modpkgs.append(modname)
931
                                        elif ispackage(path):
932
                                                modpkgs.append(file + ' (package)')
933
                        modpkgs.sort()
934
                        result = result + self.section(
935
                                'PACKAGE CONTENTS', join(modpkgs, '\n'))
1062 subik 936
 
1063 subik 937
                if classes:
938
                        classlist = map(lambda (key, value): value, classes)
939
                        contents = [self.formattree(
940
                                inspect.getclasstree(classlist, 1), name)]
941
                        for key, value in classes:
942
                                contents.append(self.document(value, key, name))
943
                        result = result + self.section('CLASSES', join(contents, '\n'))
1062 subik 944
 
1063 subik 945
                if funcs:
946
                        contents = []
947
                        for key, value in funcs:
948
                                contents.append(self.document(value, key, name))
949
                        result = result + self.section('FUNCTIONS', join(contents, '\n'))
1062 subik 950
 
1063 subik 951
                if data:
952
                        contents = []
953
                        for key, value in data:
954
                                contents.append(self.docother(value, key, name, 70))
955
                        result = result + self.section('DATA', join(contents, '\n'))
1062 subik 956
 
1063 subik 957
                if hasattr(object, '__version__'):
958
                        version = str(object.__version__)
959
                        if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
960
                                version = strip(version[11:-1])
961
                        result = result + self.section('VERSION', version)
962
                if hasattr(object, '__date__'):
963
                        result = result + self.section('DATE', str(object.__date__))
964
                if hasattr(object, '__author__'):
965
                        result = result + self.section('AUTHOR', str(object.__author__))
966
                if hasattr(object, '__credits__'):
967
                        result = result + self.section('CREDITS', str(object.__credits__))
968
                return result
1062 subik 969
 
1063 subik 970
        def docclass(self, object, name=None, mod=None):
971
                """Produce text documentation for a given class object."""
972
                realname = object.__name__
973
                name = name or realname
974
                bases = object.__bases__
1062 subik 975
 
1063 subik 976
                def makename(c, m=object.__module__):
977
                        return classname(c, m)
1062 subik 978
 
1063 subik 979
                if name == realname:
980
                        title = 'class ' + self.bold(realname)
981
                else:
982
                        title = self.bold(name) + ' = class ' + realname
983
                if bases:
984
                        parents = map(makename, bases)
985
                        title = title + '(%s)' % join(parents, ', ')
1062 subik 986
 
1063 subik 987
                doc = getdoc(object)
988
                contents = doc and [doc + '\n'] or []
989
                push = contents.append
1062 subik 990
 
1063 subik 991
                # List the mro, if non-trivial.
992
                mro = list(inspect.getmro(object))
993
                if len(mro) > 2:
994
                        push("Method resolution order:")
995
                        for base in mro:
996
                                push('  ' + makename(base))
997
                        push('')
1062 subik 998
 
1063 subik 999
                # Cute little class to pump out a horizontal rule between sections.
1000
                class HorizontalRule:
1001
                        def __init__(self):
1002
                                self.needone = 0
1003
                        def maybe(self):
1004
                                if self.needone:
1005
                                        push('-' * 70)
1006
                                self.needone = 1
1007
                hr = HorizontalRule()
1062 subik 1008
 
1063 subik 1009
                def spill(msg, attrs, predicate):
1010
                        ok, attrs = _split_list(attrs, predicate)
1011
                        if ok:
1012
                                hr.maybe()
1013
                                push(msg)
1014
                                for name, kind, homecls, value in ok:
1015
                                        push(self.document(getattr(object, name),
1016
                                                                           name, mod, object))
1017
                        return attrs
1062 subik 1018
 
1063 subik 1019
                def spillproperties(msg, attrs, predicate):
1020
                        ok, attrs = _split_list(attrs, predicate)
1021
                        if ok:
1022
                                hr.maybe()
1023
                                push(msg)
1024
                                for name, kind, homecls, value in ok:
1025
                                        push(name)
1026
                                        need_blank_after_doc = 0
1027
                                        doc = getdoc(value) or ''
1028
                                        if doc:
1029
                                                push(self.indent(doc))
1030
                                                need_blank_after_doc = 1
1031
                                        for attr, tag in [('fget', '<get>'),
1032
                                                                          ('fset', '<set>'),
1033
                                                                          ('fdel', '<delete>')]:
1034
                                                func = getattr(value, attr)
1035
                                                if func is not None:
1036
                                                        if need_blank_after_doc:
1037
                                                                push('')
1038
                                                                need_blank_after_doc = 0
1039
                                                        base = self.document(func, tag, mod)
1040
                                                        push(self.indent(base))
1041
                        return attrs
1062 subik 1042
 
1063 subik 1043
                def spilldata(msg, attrs, predicate):
1044
                        ok, attrs = _split_list(attrs, predicate)
1045
                        if ok:
1046
                                hr.maybe()
1047
                                push(msg)
1048
                                for name, kind, homecls, value in ok:
1049
                                        if callable(value) or inspect.isdatadescriptor(value):
1050
                                                doc = getattr(value, "__doc__", None)
1051
                                        else:
1052
                                                doc = None
1053
                                        push(self.docother(getattr(object, name),
1054
                                                                           name, mod, 70, doc) + '\n')
1055
                        return attrs
1062 subik 1056
 
1063 subik 1057
                attrs = filter(lambda (name, kind, cls, value): visiblename(name),
1058
                                           inspect.classify_class_attrs(object))
1059
                while attrs:
1060
                        if mro:
1061
                                thisclass = mro.pop(0)
1062
                        else:
1063
                                thisclass = attrs[0][2]
1064
                        attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)
1062 subik 1065
 
1063 subik 1066
                        if thisclass is __builtin__.object:
1067
                                attrs = inherited
1068
                                continue
1069
                        elif thisclass is object:
1070
                                tag = "defined here"
1071
                        else:
1072
                                tag = "inherited from %s" % classname(thisclass,
1073
                                                                                                          object.__module__)
1074
                        filter(lambda t: not t[0].startswith('_'), attrs)
1062 subik 1075
 
1063 subik 1076
                        # Sort attrs by name.
1077
                        attrs.sort()
1062 subik 1078
 
1063 subik 1079
                        # Pump out the attrs, segregated by kind.
1080
                        attrs = spill("Methods %s:\n" % tag, attrs,
1081
                                                  lambda t: t[1] == 'method')
1082
                        attrs = spill("Class methods %s:\n" % tag, attrs,
1083
                                                  lambda t: t[1] == 'class method')
1084
                        attrs = spill("Static methods %s:\n" % tag, attrs,
1085
                                                  lambda t: t[1] == 'static method')
1086
                        attrs = spillproperties("Properties %s:\n" % tag, attrs,
1087
                                                                        lambda t: t[1] == 'property')
1088
                        attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
1089
                                                          lambda t: t[1] == 'data')
1090
                        assert attrs == []
1091
                        attrs = inherited
1062 subik 1092
 
1063 subik 1093
                contents = '\n'.join(contents)
1094
                if not contents:
1095
                        return title + '\n'
1096
                return title + '\n' + self.indent(rstrip(contents), ' |  ') + '\n'
1062 subik 1097
 
1063 subik 1098
        def formatvalue(self, object):
1099
                """Format an argument default value as text."""
1100
                return '=' + self.repr(object)
1062 subik 1101
 
1063 subik 1102
        def docroutine(self, object, name=None, mod=None, cl=None):
1103
                """Produce text documentation for a function or method object."""
1104
                realname = object.__name__
1105
                name = name or realname
1106
                note = ''
1107
                skipdocs = 0
1108
                if inspect.ismethod(object):
1109
                        imclass = object.im_class
1110
                        if cl:
1111
                                if imclass is not cl:
1112
                                        note = ' from ' + classname(imclass, mod)
1113
                        else:
1114
                                if object.im_self:
1115
                                        note = ' method of %s instance' % classname(
1116
                                                object.im_self.__class__, mod)
1117
                                else:
1118
                                        note = ' unbound %s method' % classname(imclass,mod)
1119
                        object = object.im_func
1062 subik 1120
 
1063 subik 1121
                if name == realname:
1122
                        title = self.bold(realname)
1123
                else:
1124
                        if (cl and realname in cl.__dict__ and
1125
                                cl.__dict__[realname] is object):
1126
                                skipdocs = 1
1127
                        title = self.bold(name) + ' = ' + realname
1128
                if inspect.isfunction(object):
1129
                        args, varargs, varkw, defaults = inspect.getargspec(object)
1130
                        argspec = inspect.formatargspec(
1131
                                args, varargs, varkw, defaults, formatvalue=self.formatvalue)
1132
                        if realname == '<lambda>':
1133
                                title = 'lambda'
1134
                                argspec = argspec[1:-1] # remove parentheses
1135
                else:
1136
                        argspec = '(...)'
1137
                decl = title + argspec + note
1062 subik 1138
 
1063 subik 1139
                if skipdocs:
1140
                        return decl + '\n'
1141
                else:
1142
                        doc = getdoc(object) or ''
1143
                        return decl + '\n' + (doc and rstrip(self.indent(doc)) + '\n')
1062 subik 1144
 
1063 subik 1145
        def docother(self, object, name=None, mod=None, maxlen=None, doc=None):
1146
                """Produce text documentation for a data object."""
1147
                repr = self.repr(object)
1148
                if maxlen:
1149
                        line = (name and name + ' = ' or '') + repr
1150
                        chop = maxlen - len(line)
1151
                        if chop < 0: repr = repr[:chop] + '...'
1152
                line = (name and self.bold(name) + ' = ' or '') + repr
1153
                if doc is not None:
1154
                        line += '\n' + self.indent(str(doc))
1155
                return line
1062 subik 1156
 
1157
# --------------------------------------------------------- user interfaces
1158
 
1159
def pager(text):
1063 subik 1160
        """The first time this is called, determine what kind of pager to use."""
1161
        global pager
1162
        pager = getpager()
1163
        pager(text)
1062 subik 1164
 
1165
def getpager():
1063 subik 1166
        """Decide what method to use for paging through text."""
1167
        if type(sys.stdout) is not types.FileType:
1168
                return plainpager
1169
        if not sys.stdin.isatty() or not sys.stdout.isatty():
1170
                return plainpager
1171
        if os.environ.get('TERM') in ['dumb', 'emacs']:
1172
                return plainpager
1173
        if 'PAGER' in os.environ:
1174
                if sys.platform == 'win32': # pipes completely broken in Windows
1175
                        return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
1176
                elif os.environ.get('TERM') in ['dumb', 'emacs']:
1177
                        return lambda text: pipepager(plain(text), os.environ['PAGER'])
1178
                else:
1179
                        return lambda text: pipepager(text, os.environ['PAGER'])
1180
        if sys.platform == 'win32' or sys.platform.startswith('os2'):
1181
                return lambda text: tempfilepager(plain(text), 'more <')
1182
        if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
1183
                return lambda text: pipepager(text, 'less')
1062 subik 1184
 
1063 subik 1185
        import tempfile
1186
        (fd, filename) = tempfile.mkstemp()
1187
        os.close(fd)
1188
        try:
1189
                if hasattr(os, 'system') and os.system('more %s' % filename) == 0:
1190
                        return lambda text: pipepager(text, 'more')
1191
                else:
1192
                        return ttypager
1193
        finally:
1194
                os.unlink(filename)
1062 subik 1195
 
1196
def plain(text):
1063 subik 1197
        """Remove boldface formatting from text."""
1198
        return re.sub('.\b', '', text)
1062 subik 1199
 
1200
def pipepager(text, cmd):
1063 subik 1201
        """Page through text by feeding it to another program."""
1202
        pipe = os.popen(cmd, 'w')
1203
        try:
1204
                pipe.write(text)
1205
                pipe.close()
1206
        except IOError:
1207
                pass # Ignore broken pipes caused by quitting the pager program.
1062 subik 1208
 
1209
def tempfilepager(text, cmd):
1063 subik 1210
        """Page through text by invoking a program on a temporary file."""
1211
        import tempfile
1212
        filename = tempfile.mktemp()
1213
        file = open(filename, 'w')
1214
        file.write(text)
1215
        file.close()
1216
        try:
1217
                os.system(cmd + ' ' + filename)
1218
        finally:
1219
                os.unlink(filename)
1062 subik 1220
 
1221
def ttypager(text):
1063 subik 1222
        """Page through text on a text terminal."""
1223
        lines = split(plain(text), '\n')
1224
        try:
1225
                import tty
1226
                fd = sys.stdin.fileno()
1227
                old = tty.tcgetattr(fd)
1228
                tty.setcbreak(fd)
1229
                getchar = lambda: sys.stdin.read(1)
1230
        except (ImportError, AttributeError):
1231
                tty = None
1232
                getchar = lambda: sys.stdin.readline()[:-1][:1]
1062 subik 1233
 
1063 subik 1234
        try:
1235
                r = inc = os.environ.get('LINES', 25) - 1
1236
                sys.stdout.write(join(lines[:inc], '\n') + '\n')
1237
                while lines[r:]:
1238
                        sys.stdout.write('-- more --')
1239
                        sys.stdout.flush()
1240
                        c = getchar()
1062 subik 1241
 
1063 subik 1242
                        if c in ['q', 'Q']:
1243
                                sys.stdout.write('\r              \r')
1244
                                break
1245
                        elif c in ['\r', '\n']:
1246
                                sys.stdout.write('\r              \r' + lines[r] + '\n')
1247
                                r = r + 1
1248
                                continue
1249
                        if c in ['b', 'B', '\x1b']:
1250
                                r = r - inc - inc
1251
                                if r < 0: r = 0
1252
                        sys.stdout.write('\n' + join(lines[r:r+inc], '\n') + '\n')
1253
                        r = r + inc
1062 subik 1254
 
1063 subik 1255
        finally:
1256
                if tty:
1257
                        tty.tcsetattr(fd, tty.TCSAFLUSH, old)
1062 subik 1258
 
1259
def plainpager(text):
1063 subik 1260
        """Simply print unformatted text.  This is the ultimate fallback."""
1261
        sys.stdout.write(plain(text))
1062 subik 1262
 
1263
def describe(thing):
1063 subik 1264
        """Produce a short description of the given thing."""
1265
        if inspect.ismodule(thing):
1266
                if thing.__name__ in sys.builtin_module_names:
1267
                        return 'built-in module ' + thing.__name__
1268
                if hasattr(thing, '__path__'):
1269
                        return 'package ' + thing.__name__
1270
                else:
1271
                        return 'module ' + thing.__name__
1272
        if inspect.isbuiltin(thing):
1273
                return 'built-in function ' + thing.__name__
1274
        if inspect.isclass(thing):
1275
                return 'class ' + thing.__name__
1276
        if inspect.isfunction(thing):
1277
                return 'function ' + thing.__name__
1278
        if inspect.ismethod(thing):
1279
                return 'method ' + thing.__name__
1280
        if type(thing) is types.InstanceType:
1281
                return 'instance of ' + thing.__class__.__name__
1282
        return type(thing).__name__
1062 subik 1283
 
1284
def locate(path, forceload=0):
1063 subik 1285
        """Locate an object by name or dotted path, importing as necessary."""
1286
        parts = [part for part in split(path, '.') if part]
1287
        module, n = None, 0
1288
        while n < len(parts):
1289
                nextmodule = safeimport(join(parts[:n+1], '.'), forceload)
1290
                if nextmodule: module, n = nextmodule, n + 1
1291
                else: break
1292
        if module:
1293
                object = module
1294
                for part in parts[n:]:
1295
                        try: object = getattr(object, part)
1296
                        except AttributeError: return None
1297
                return object
1298
        else:
1299
                if hasattr(__builtin__, path):
1300
                        return getattr(__builtin__, path)
1062 subik 1301
 
1302
# --------------------------------------- interactive interpreter interface
1303
 
1304
text = TextDoc()
1305
html = HTMLDoc()
1306
 
1307
def resolve(thing, forceload=0):
1063 subik 1308
        """Given an object or a path to an object, get the object and its name."""
1309
        if isinstance(thing, str):
1310
                object = locate(thing, forceload)
1311
                if not object:
1312
                        raise ImportError, 'no Python documentation found for %r' % thing
1313
                return object, thing
1314
        else:
1315
                return thing, getattr(thing, '__name__', None)
1062 subik 1316
 
1317
def doc(thing, title='Python Library Documentation: %s', forceload=0):
1063 subik 1318
        """Display text documentation, given an object or a path to an object."""
1319
        try:
1320
                object, name = resolve(thing, forceload)
1321
                desc = describe(object)
1322
                module = inspect.getmodule(object)
1323
                if name and '.' in name:
1324
                        desc += ' in ' + name[:name.rfind('.')]
1325
                elif module and module is not object:
1326
                        desc += ' in module ' + module.__name__
1327
                pager(title % desc + '\n\n' + text.document(object, name))
1328
        except (ImportError, ErrorDuringImport), value:
1329
                print value
1062 subik 1330
 
1331
def writedoc(thing, forceload=0):
1063 subik 1332
        """Write HTML documentation to a file in the current directory."""
1333
        try:
1334
                object, name = resolve(thing, forceload)
1335
                page = html.page(describe(object), html.document(object, name))
1336
                file = open(name + '.html', 'w')
1337
                file.write(page)
1338
                file.close()
1339
                print 'wrote', name + '.html'
1340
        except (ImportError, ErrorDuringImport), value:
1341
                print value
1062 subik 1342
 
1343
def writedocs(dir, pkgpath='', done=None):
1063 subik 1344
        """Write out HTML documentation for all modules in a directory tree."""
1345
        if done is None: done = {}
1346
        for file in os.listdir(dir):
1347
                path = os.path.join(dir, file)
1348
                if ispackage(path):
1349
                        writedocs(path, pkgpath + file + '.', done)
1350
                elif os.path.isfile(path):
1351
                        modname = inspect.getmodulename(path)
1352
                        if modname:
1353
                                if modname == '__init__':
1354
                                        modname = pkgpath[:-1] # remove trailing period
1355
                                else:
1356
                                        modname = pkgpath + modname
1357
                                if modname not in done:
1358
                                        done[modname] = 1
1359
                                        writedoc(modname)
1062 subik 1360
 
1361
class Helper:
1063 subik 1362
        keywords = {
1363
                'and': 'BOOLEAN',
1364
                'assert': ('ref/assert', ''),
1365
                'break': ('ref/break', 'while for'),
1366
                'class': ('ref/class', 'CLASSES SPECIALMETHODS'),
1367
                'continue': ('ref/continue', 'while for'),
1368
                'def': ('ref/function', ''),
1369
                'del': ('ref/del', 'BASICMETHODS'),
1370
                'elif': 'if',
1371
                'else': ('ref/if', 'while for'),
1372
                'except': 'try',
1373
                'exec': ('ref/exec', ''),
1374
                'finally': 'try',
1375
                'for': ('ref/for', 'break continue while'),
1376
                'from': 'import',
1377
                'global': ('ref/global', 'NAMESPACES'),
1378
                'if': ('ref/if', 'TRUTHVALUE'),
1379
                'import': ('ref/import', 'MODULES'),
1380
                'in': ('ref/comparisons', 'SEQUENCEMETHODS2'),
1381
                'is': 'COMPARISON',
1382
                'lambda': ('ref/lambdas', 'FUNCTIONS'),
1383
                'not': 'BOOLEAN',
1384
                'or': 'BOOLEAN',
1385
                'pass': ('ref/pass', ''),
1386
                'print': ('ref/print', ''),
1387
                'raise': ('ref/raise', 'EXCEPTIONS'),
1388
                'return': ('ref/return', 'FUNCTIONS'),
1389
                'try': ('ref/try', 'EXCEPTIONS'),
1390
                'while': ('ref/while', 'break continue if TRUTHVALUE'),
1391
                'yield': ('ref/yield', ''),
1392
        }
1062 subik 1393
 
1063 subik 1394
        topics = {
1395
                'TYPES': ('ref/types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS FUNCTIONS CLASSES MODULES FILES inspect'),
1396
                'STRINGS': ('ref/strings', 'str UNICODE SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1397
                'STRINGMETHODS': ('lib/string-methods', 'STRINGS FORMATTING'),
1398
                'FORMATTING': ('lib/typesseq-strings', 'OPERATORS'),
1399
                'UNICODE': ('ref/strings', 'encodings unicode SEQUENCES STRINGMETHODS FORMATTING TYPES'),
1400
                'NUMBERS': ('ref/numbers', 'INTEGER FLOAT COMPLEX TYPES'),
1401
                'INTEGER': ('ref/integers', 'int range'),
1402
                'FLOAT': ('ref/floating', 'float math'),
1403
                'COMPLEX': ('ref/imaginary', 'complex cmath'),
1404
                'SEQUENCES': ('lib/typesseq', 'STRINGMETHODS FORMATTING xrange LISTS'),
1405
                'MAPPINGS': 'DICTIONARIES',
1406
                'FUNCTIONS': ('lib/typesfunctions', 'def TYPES'),
1407
                'METHODS': ('lib/typesmethods', 'class def CLASSES TYPES'),
1408
                'CODEOBJECTS': ('lib/bltin-code-objects', 'compile FUNCTIONS TYPES'),
1409
                'TYPEOBJECTS': ('lib/bltin-type-objects', 'types TYPES'),
1410
                'FRAMEOBJECTS': 'TYPES',
1411
                'TRACEBACKS': 'TYPES',
1412
                'NONE': ('lib/bltin-null-object', ''),
1413
                'ELLIPSIS': ('lib/bltin-ellipsis-object', 'SLICINGS'),
1414
                'FILES': ('lib/bltin-file-objects', ''),
1415
                'SPECIALATTRIBUTES': ('lib/specialattrs', ''),
1416
                'CLASSES': ('ref/types', 'class SPECIALMETHODS PRIVATENAMES'),
1417
                'MODULES': ('lib/typesmodules', 'import'),
1418
                'PACKAGES': 'import',
1419
                'EXPRESSIONS': ('ref/summary', 'lambda or and not in is BOOLEAN COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES LISTS DICTIONARIES BACKQUOTES'),
1420
                'OPERATORS': 'EXPRESSIONS',
1421
                'PRECEDENCE': 'EXPRESSIONS',
1422
                'OBJECTS': ('ref/objects', 'TYPES'),
1423
                'SPECIALMETHODS': ('ref/specialnames', 'BASICMETHODS ATTRIBUTEMETHODS CALLABLEMETHODS SEQUENCEMETHODS1 MAPPINGMETHODS SEQUENCEMETHODS2 NUMBERMETHODS CLASSES'),
1424
                'BASICMETHODS': ('ref/customization', 'cmp hash repr str SPECIALMETHODS'),
1425
                'ATTRIBUTEMETHODS': ('ref/attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
1426
                'CALLABLEMETHODS': ('ref/callable-types', 'CALLS SPECIALMETHODS'),
1427
                'SEQUENCEMETHODS1': ('ref/sequence-types', 'SEQUENCES SEQUENCEMETHODS2 SPECIALMETHODS'),
1428
                'SEQUENCEMETHODS2': ('ref/sequence-methods', 'SEQUENCES SEQUENCEMETHODS1 SPECIALMETHODS'),
1429
                'MAPPINGMETHODS': ('ref/sequence-types', 'MAPPINGS SPECIALMETHODS'),
1430
                'NUMBERMETHODS': ('ref/numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT SPECIALMETHODS'),
1431
                'EXECUTION': ('ref/execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
1432
                'NAMESPACES': ('ref/naming', 'global ASSIGNMENT DELETION DYNAMICFEATURES'),
1433
                'DYNAMICFEATURES': ('ref/dynamic-features', ''),
1434
                'SCOPING': 'NAMESPACES',
1435
                'FRAMES': 'NAMESPACES',
1436
                'EXCEPTIONS': ('ref/exceptions', 'try except finally raise'),
1437
                'COERCIONS': ('ref/coercion-rules','CONVERSIONS'),
1438
                'CONVERSIONS': ('ref/conversions', 'COERCIONS'),
1439
                'IDENTIFIERS': ('ref/identifiers', 'keywords SPECIALIDENTIFIERS'),
1440
                'SPECIALIDENTIFIERS': ('ref/id-classes', ''),
1441
                'PRIVATENAMES': ('ref/atom-identifiers', ''),
1442
                'LITERALS': ('ref/atom-literals', 'STRINGS BACKQUOTES NUMBERS TUPLELITERALS LISTLITERALS DICTIONARYLITERALS'),
1443
                'TUPLES': 'SEQUENCES',
1444
                'TUPLELITERALS': ('ref/exprlists', 'TUPLES LITERALS'),
1445
                'LISTS': ('lib/typesseq-mutable', 'LISTLITERALS'),
1446
                'LISTLITERALS': ('ref/lists', 'LISTS LITERALS'),
1447
                'DICTIONARIES': ('lib/typesmapping', 'DICTIONARYLITERALS'),
1448
                'DICTIONARYLITERALS': ('ref/dict', 'DICTIONARIES LITERALS'),
1449
                'BACKQUOTES': ('ref/string-conversions', 'repr str STRINGS LITERALS'),
1450
                'ATTRIBUTES': ('ref/attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
1451
                'SUBSCRIPTS': ('ref/subscriptions', 'SEQUENCEMETHODS1'),
1452
                'SLICINGS': ('ref/slicings', 'SEQUENCEMETHODS2'),
1453
                'CALLS': ('ref/calls', 'EXPRESSIONS'),
1454
                'POWER': ('ref/power', 'EXPRESSIONS'),
1455
                'UNARY': ('ref/unary', 'EXPRESSIONS'),
1456
                'BINARY': ('ref/binary', 'EXPRESSIONS'),
1457
                'SHIFTING': ('ref/shifting', 'EXPRESSIONS'),
1458
                'BITWISE': ('ref/bitwise', 'EXPRESSIONS'),
1459
                'COMPARISON': ('ref/comparisons', 'EXPRESSIONS BASICMETHODS'),
1460
                'BOOLEAN': ('ref/Booleans', 'EXPRESSIONS TRUTHVALUE'),
1461
                'ASSERTION': 'assert',
1462
                'ASSIGNMENT': ('ref/assignment', 'AUGMENTEDASSIGNMENT'),
1463
                'AUGMENTEDASSIGNMENT': ('ref/augassign', 'NUMBERMETHODS'),
1464
                'DELETION': 'del',
1465
                'PRINTING': 'print',
1466
                'RETURNING': 'return',
1467
                'IMPORTING': 'import',
1468
                'CONDITIONAL': 'if',
1469
                'LOOPING': ('ref/compound', 'for while break continue'),
1470
                'TRUTHVALUE': ('lib/truth', 'if while and or not BASICMETHODS'),
1471
                'DEBUGGING': ('lib/module-pdb', 'pdb'),
1472
        }
1062 subik 1473
 
1063 subik 1474
        def __init__(self, input, output):
1475
                self.input = input
1476
                self.output = output
1477
                self.docdir = None
1478
                execdir = os.path.dirname(sys.executable)
1479
                homedir = os.environ.get('PYTHONHOME')
1480
                for dir in [os.environ.get('PYTHONDOCS'),
1481
                                        homedir and os.path.join(homedir, 'doc'),
1482
                                        os.path.join(execdir, 'doc'),
1483
                                        '/usr/doc/python-docs-' + split(sys.version)[0],
1484
                                        '/usr/doc/python-' + split(sys.version)[0],
1485
                                        '/usr/doc/python-docs-' + sys.version[:3],
1486
                                        '/usr/doc/python-' + sys.version[:3],
1487
                                        os.path.join(sys.prefix, 'Resources/English.lproj/Documentation')]:
1488
                        if dir and os.path.isdir(os.path.join(dir, 'lib')):
1489
                                self.docdir = dir
1062 subik 1490
 
1063 subik 1491
        def __repr__(self):
1492
                if inspect.stack()[1][3] == '?':
1493
                        self()
1494
                        return ''
1495
                return '<pydoc.Helper instance>'
1062 subik 1496
 
1063 subik 1497
        def __call__(self, request=None):
1498
                if request is not None:
1499
                        self.help(request)
1500
                else:
1501
                        self.intro()
1502
                        self.interact()
1503
                        self.output.write('''
1062 subik 1504
You are now leaving help and returning to the Python interpreter.
1505
If you want to ask for help on a particular object directly from the
1506
interpreter, you can type "help(object)".  Executing "help('string')"
1507
has the same effect as typing a particular string at the help> prompt.
1508
''')
1509
 
1063 subik 1510
        def interact(self):
1511
                self.output.write('\n')
1512
                while True:
1513
                        self.output.write('help> ')
1514
                        self.output.flush()
1515
                        try:
1516
                                request = self.input.readline()
1517
                                if not request: break
1518
                        except KeyboardInterrupt: break
1519
                        request = strip(replace(request, '"', '', "'", ''))
1520
                        if lower(request) in ['q', 'quit']: break
1521
                        self.help(request)
1062 subik 1522
 
1063 subik 1523
        def help(self, request):
1524
                if type(request) is type(''):
1525
                        if request == 'help': self.intro()
1526
                        elif request == 'keywords': self.listkeywords()
1527
                        elif request == 'topics': self.listtopics()
1528
                        elif request == 'modules': self.listmodules()
1529
                        elif request[:8] == 'modules ':
1530
                                self.listmodules(split(request)[1])
1531
                        elif request in self.keywords: self.showtopic(request)
1532
                        elif request in self.topics: self.showtopic(request)
1533
                        elif request: doc(request, 'Help on %s:')
1534
                elif isinstance(request, Helper): self()
1535
                else: doc(request, 'Help on %s:')
1536
                self.output.write('\n')
1062 subik 1537
 
1063 subik 1538
        def intro(self):
1539
                self.output.write('''
1062 subik 1540
Welcome to Python %s!  This is the online help utility.
1541
 
1542
If this is your first time using Python, you should definitely check out
1543
the tutorial on the Internet at http://www.python.org/doc/tut/.
1544
 
1545
Enter the name of any module, keyword, or topic to get help on writing
1546
Python programs and using Python modules.  To quit this help utility and
1547
return to the interpreter, just type "quit".
1548
 
1549
To get a list of available modules, keywords, or topics, type "modules",
1550
"keywords", or "topics".  Each module also comes with a one-line summary
1551
of what it does; to list the modules whose summaries contain a given word
1552
such as "spam", type "modules spam".
1553
''' % sys.version[:3])
1554
 
1063 subik 1555
        def list(self, items, columns=4, width=80):
1556
                items = items[:]
1557
                items.sort()
1558
                colw = width / columns
1559
                rows = (len(items) + columns - 1) / columns
1560
                for row in range(rows):
1561
                        for col in range(columns):
1562
                                i = col * rows + row
1563
                                if i < len(items):
1564
                                        self.output.write(items[i])
1565
                                        if col < columns - 1:
1566
                                                self.output.write(' ' + ' ' * (colw-1 - len(items[i])))
1567
                        self.output.write('\n')
1062 subik 1568
 
1063 subik 1569
        def listkeywords(self):
1570
                self.output.write('''
1062 subik 1571
Here is a list of the Python keywords.  Enter any keyword to get more help.
1572
 
1573
''')
1063 subik 1574
                self.list(self.keywords.keys())
1062 subik 1575
 
1063 subik 1576
        def listtopics(self):
1577
                self.output.write('''
1062 subik 1578
Here is a list of available topics.  Enter any topic name to get more help.
1579
 
1580
''')
1063 subik 1581
                self.list(self.topics.keys())
1062 subik 1582
 
1063 subik 1583
        def showtopic(self, topic):
1584
                if not self.docdir:
1585
                        self.output.write('''
1062 subik 1586
Sorry, topic and keyword documentation is not available because the Python
1587
HTML documentation files could not be found.  If you have installed them,
1588
please set the environment variable PYTHONDOCS to indicate their location.
1589
''')
1063 subik 1590
                        return
1591
                target = self.topics.get(topic, self.keywords.get(topic))
1592
                if not target:
1593
                        self.output.write('no documentation found for %s\n' % repr(topic))
1594
                        return
1595
                if type(target) is type(''):
1596
                        return self.showtopic(target)
1062 subik 1597
 
1063 subik 1598
                filename, xrefs = target
1599
                filename = self.docdir + '/' + filename + '.html'
1600
                try:
1601
                        file = open(filename)
1602
                except:
1603
                        self.output.write('could not read docs from %s\n' % filename)
1604
                        return
1062 subik 1605
 
1063 subik 1606
                divpat = re.compile('<div[^>]*navigat.*?</div.*?>', re.I | re.S)
1607
                addrpat = re.compile('<address.*?>.*?</address.*?>', re.I | re.S)
1608
                document = re.sub(addrpat, '', re.sub(divpat, '', file.read()))
1609
                file.close()
1062 subik 1610
 
1063 subik 1611
                import htmllib, formatter, StringIO
1612
                buffer = StringIO.StringIO()
1613
                parser = htmllib.HTMLParser(
1614
                        formatter.AbstractFormatter(formatter.DumbWriter(buffer)))
1615
                parser.start_table = parser.do_p
1616
                parser.end_table = lambda parser=parser: parser.do_p({})
1617
                parser.start_tr = parser.do_br
1618
                parser.start_td = parser.start_th = lambda a, b=buffer: b.write('\t')
1619
                parser.feed(document)
1620
                buffer = replace(buffer.getvalue(), '\xa0', ' ', '\n', '\n  ')
1621
                pager('  ' + strip(buffer) + '\n')
1622
                if xrefs:
1623
                        buffer = StringIO.StringIO()
1624
                        formatter.DumbWriter(buffer).send_flowing_data(
1625
                                'Related help topics: ' + join(split(xrefs), ', ') + '\n')
1626
                        self.output.write('\n%s\n' % buffer.getvalue())
1062 subik 1627
 
1063 subik 1628
        def listmodules(self, key=''):
1629
                if key:
1630
                        self.output.write('''
1062 subik 1631
Here is a list of matching modules.  Enter any module name to get more help.
1632
 
1633
''')
1063 subik 1634
                        apropos(key)
1635
                else:
1636
                        self.output.write('''
1062 subik 1637
Please wait a moment while I gather a list of all available modules...
1638
 
1639
''')
1063 subik 1640
                        modules = {}
1641
                        def callback(path, modname, desc, modules=modules):
1642
                                if modname and modname[-9:] == '.__init__':
1643
                                        modname = modname[:-9] + ' (package)'
1644
                                if find(modname, '.') < 0:
1645
                                        modules[modname] = 1
1646
                        ModuleScanner().run(callback)
1647
                        self.list(modules.keys())
1648
                        self.output.write('''
1062 subik 1649
Enter any module name to get more help.  Or, type "modules spam" to search
1650
for modules whose descriptions contain the word "spam".
1651
''')
1652
 
1653
help = Helper(sys.stdin, sys.stdout)
1654
 
1655
class Scanner:
1063 subik 1656
        """A generic tree iterator."""
1657
        def __init__(self, roots, children, descendp):
1658
                self.roots = roots[:]
1659
                self.state = []
1660
                self.children = children
1661
                self.descendp = descendp
1062 subik 1662
 
1063 subik 1663
        def next(self):
1664
                if not self.state:
1665
                        if not self.roots:
1666
                                return None
1667
                        root = self.roots.pop(0)
1668
                        self.state = [(root, self.children(root))]
1669
                node, children = self.state[-1]
1670
                if not children:
1671
                        self.state.pop()
1672
                        return self.next()
1673
                child = children.pop(0)
1674
                if self.descendp(child):
1675
                        self.state.append((child, self.children(child)))
1676
                return child
1062 subik 1677
 
1678
class ModuleScanner(Scanner):
1063 subik 1679
        """An interruptible scanner that searches module synopses."""
1680
        def __init__(self):
1681
                roots = map(lambda dir: (dir, ''), pathdirs())
1682
                Scanner.__init__(self, roots, self.submodules, self.isnewpackage)
1683
                self.inodes = map(lambda (dir, pkg): os.stat(dir).st_ino, roots)
1062 subik 1684
 
1063 subik 1685
        def submodules(self, (dir, package)):
1686
                children = []
1687
                for file in os.listdir(dir):
1688
                        path = os.path.join(dir, file)
1689
                        if ispackage(path):
1690
                                children.append((path, package + (package and '.') + file))
1691
                        else:
1692
                                children.append((path, package))
1693
                children.sort() # so that spam.py comes before spam.pyc or spam.pyo
1694
                return children
1062 subik 1695
 
1063 subik 1696
        def isnewpackage(self, (dir, package)):
1697
                inode = os.path.exists(dir) and os.stat(dir).st_ino
1698
                if not (os.path.islink(dir) and inode in self.inodes):
1699
                        self.inodes.append(inode) # detect circular symbolic links
1700
                        return ispackage(dir)
1701
                return False
1062 subik 1702
 
1063 subik 1703
        def run(self, callback, key=None, completer=None):
1704
                if key: key = lower(key)
1705
                self.quit = False
1706
                seen = {}
1062 subik 1707
 
1063 subik 1708
                for modname in sys.builtin_module_names:
1709
                        if modname != '__main__':
1710
                                seen[modname] = 1
1711
                                if key is None:
1712
                                        callback(None, modname, '')
1713
                                else:
1714
                                        desc = split(__import__(modname).__doc__ or '', '\n')[0]
1715
                                        if find(lower(modname + ' - ' + desc), key) >= 0:
1716
                                                callback(None, modname, desc)
1062 subik 1717
 
1063 subik 1718
                while not self.quit:
1719
                        node = self.next()
1720
                        if not node: break
1721
                        path, package = node
1722
                        modname = inspect.getmodulename(path)
1723
                        if os.path.isfile(path) and modname:
1724
                                modname = package + (package and '.') + modname
1725
                                if not modname in seen:
1726
                                        seen[modname] = 1 # if we see spam.py, skip spam.pyc
1727
                                        if key is None:
1728
                                                callback(path, modname, '')
1729
                                        else:
1730
                                                desc = synopsis(path) or ''
1731
                                                if find(lower(modname + ' - ' + desc), key) >= 0:
1732
                                                        callback(path, modname, desc)
1733
                if completer: completer()
1062 subik 1734
 
1735
def apropos(key):
1063 subik 1736
        """Print all the one-line module summaries that contain a substring."""
1737
        def callback(path, modname, desc):
1738
                if modname[-9:] == '.__init__':
1739
                        modname = modname[:-9] + ' (package)'
1740
                print modname, desc and '- ' + desc
1741
        try: import warnings
1742
        except ImportError: pass
1743
        else: warnings.filterwarnings('ignore') # ignore problems during import
1744
        ModuleScanner().run(callback, key)
1062 subik 1745
 
1746
 
1747
################################################## make it
1748
 
1749
import scribus
1750
import sys
1751
sys.setappdefaultencoding("utf8")
1752
 
1753
writedoc(scribus)
1754
writedoc(scribus.PDFfile)
1755
writedoc(scribus.Printer)