~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/reportlab/tools/docco/docpy.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python2.3
 
2
#Copyright ReportLab Europe Ltd. 2000-2004
 
3
#see license.txt for license details
 
4
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/docpy.py
 
5
 
 
6
"""Generate documentation from live Python objects.
 
7
 
 
8
This is an evolving module that allows to generate documentation
 
9
for python modules in an automated fashion. The idea is to take
 
10
live Python objects and inspect them in order to use as much mean-
 
11
ingful information as possible to write in some formatted way into
 
12
different types of documents.
 
13
 
 
14
In principle a skeleton captures the gathered information and
 
15
makes it available via a certain API to formatters that use it
 
16
in whatever way they like to produce something of interest. The
 
17
API allows for adding behaviour in subclasses of these formatters,
 
18
such that, e.g. for certain classes it is possible to trigger
 
19
special actions like displaying a sample image of a class that
 
20
represents some graphical widget, say.
 
21
 
 
22
Type the following for usage info:
 
23
 
 
24
  python docpy.py -h
 
25
"""
 
26
 
 
27
# Much inspired by Ka-Ping Yee's htmldoc.py.
 
28
# Needs the inspect module.
 
29
 
 
30
# Dinu Gherman
 
31
 
 
32
 
 
33
__version__ = '0.8'
 
34
 
 
35
 
 
36
import sys, os, re, types, string, getopt, copy, time
 
37
from string import find, join, split, replace, expandtabs, rstrip
 
38
 
 
39
from reportlab.pdfgen import canvas
 
40
from reportlab.lib import colors
 
41
from reportlab.lib.units import inch, cm
 
42
from reportlab.lib.pagesizes import A4
 
43
from reportlab.lib import enums
 
44
from reportlab.lib.enums import TA_CENTER, TA_LEFT
 
45
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
 
46
from reportlab.platypus.flowables import Flowable, Spacer
 
47
from reportlab.platypus.paragraph import Paragraph
 
48
from reportlab.platypus.flowables \
 
49
     import Flowable, Preformatted,Spacer, Image, KeepTogether, PageBreak
 
50
from reportlab.platypus.tableofcontents import TableOfContents
 
51
from reportlab.platypus.xpreformatted import XPreformatted
 
52
from reportlab.platypus.frames import Frame
 
53
from reportlab.platypus.doctemplate \
 
54
     import PageTemplate, BaseDocTemplate
 
55
from reportlab.platypus.tables import TableStyle, Table
 
56
import inspect
 
57
 
 
58
####################################################################
 
59
#
 
60
# Stuff needed for building PDF docs.
 
61
#
 
62
####################################################################
 
63
 
 
64
 
 
65
def mainPageFrame(canvas, doc):
 
66
    "The page frame used for all PDF documents."
 
67
 
 
68
    canvas.saveState()
 
69
 
 
70
    pageNumber = canvas.getPageNumber()
 
71
    canvas.line(2*cm, A4[1]-2*cm, A4[0]-2*cm, A4[1]-2*cm)
 
72
    canvas.line(2*cm, 2*cm, A4[0]-2*cm, 2*cm)
 
73
    if pageNumber > 1:
 
74
        canvas.setFont('Times-Roman', 12)
 
75
        canvas.drawString(4 * inch, cm, "%d" % pageNumber)
 
76
        if hasattr(canvas, 'headerLine'): # hackish
 
77
            headerline = string.join(canvas.headerLine, ' \215 ') # bullet
 
78
            canvas.drawString(2*cm, A4[1]-1.75*cm, headerline)
 
79
 
 
80
    canvas.setFont('Times-Roman', 8)
 
81
    msg = "Generated with reportlab.lib.docpy. See http://www.reportlab.com!"
 
82
    canvas.drawString(2*cm, 1.65*cm, msg)
 
83
 
 
84
    canvas.restoreState()
 
85
 
 
86
 
 
87
class MyTemplate(BaseDocTemplate):
 
88
    "The document template used for all PDF documents."
 
89
 
 
90
    _invalidInitArgs = ('pageTemplates',)
 
91
 
 
92
    def __init__(self, filename, **kw):
 
93
        frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
 
94
        self.allowSplitting = 0
 
95
        apply(BaseDocTemplate.__init__, (self, filename), kw)
 
96
        self.addPageTemplates(PageTemplate('normal', [frame1], mainPageFrame))
 
97
 
 
98
 
 
99
    def afterFlowable(self, flowable):
 
100
        "Takes care of header line, TOC and outline entries."
 
101
 
 
102
        if flowable.__class__.__name__ == 'Paragraph':
 
103
            f = flowable
 
104
            name7 = f.style.name[:7]
 
105
            name8 = f.style.name[:8]
 
106
 
 
107
            # Build a list of heading parts.
 
108
            # So far, this is the *last* item on the *previous* page...
 
109
            if name7 == 'Heading' and not hasattr(self.canv, 'headerLine'):
 
110
                self.canv.headerLine = []
 
111
 
 
112
            if name8 == 'Heading0':
 
113
                self.canv.headerLine = [f.text] # hackish
 
114
            elif name8 == 'Heading1':
 
115
                if len(self.canv.headerLine) == 2:
 
116
                    del self.canv.headerLine[-1]
 
117
                elif len(self.canv.headerLine) == 3:
 
118
                    del self.canv.headerLine[-1]
 
119
                    del self.canv.headerLine[-1]
 
120
                self.canv.headerLine.append(f.text)
 
121
            elif name8 == 'Heading2':
 
122
                if len(self.canv.headerLine) == 3:
 
123
                    del self.canv.headerLine[-1]
 
124
                self.canv.headerLine.append(f.text)
 
125
 
 
126
            if name7 == 'Heading':
 
127
                # Register TOC entries.
 
128
                headLevel = int(f.style.name[7:])
 
129
                self.notify('TOCEntry', (headLevel, flowable.getPlainText(), self.page))
 
130
 
 
131
                # Add PDF outline entries.
 
132
                c = self.canv
 
133
                title = f.text
 
134
                key = str(hash(f))
 
135
 
 
136
                try:
 
137
                    if headLevel == 0:
 
138
                        isClosed = 0
 
139
                    else:
 
140
                        isClosed = 1
 
141
 
 
142
                    c.bookmarkPage(key)
 
143
                    c.addOutlineEntry(title, key, level=headLevel,
 
144
                                      closed=isClosed)
 
145
                except ValueError:
 
146
                    pass
 
147
 
 
148
 
 
149
####################################################################
 
150
#
 
151
# Utility functions (Ka-Ping Yee).
 
152
#
 
153
####################################################################
 
154
 
 
155
def htmlescape(text):
 
156
    "Escape special HTML characters, namely &, <, >."
 
157
    return replace(replace(replace(text, '&', '&amp;'),
 
158
                                         '<', '&lt;'),
 
159
                                         '>', '&gt;')
 
160
 
 
161
def htmlrepr(object):
 
162
    return htmlescape(repr(object))
 
163
 
 
164
 
 
165
def defaultformat(object):
 
166
    return '=' + htmlrepr(object)
 
167
 
 
168
 
 
169
def getdoc(object):
 
170
    result = inspect.getdoc(object)
 
171
    if not result:
 
172
        try:
 
173
            result = inspect.getcomments(object)
 
174
        except:
 
175
            pass
 
176
    return result and rstrip(result) + '\n' or ''
 
177
 
 
178
 
 
179
def reduceDocStringLength(docStr):
 
180
    "Return first line of a multiline string."
 
181
 
 
182
    return split(docStr, '\n')[0]
 
183
 
 
184
 
 
185
####################################################################
 
186
#
 
187
# More utility functions
 
188
#
 
189
####################################################################
 
190
 
 
191
def makeHtmlSection(text, bgcolor='#FFA0FF'):
 
192
    """Create HTML code for a section.
 
193
 
 
194
    This is usually a header for all classes or functions.
 
195
u    """
 
196
    text = htmlescape(expandtabs(text))
 
197
    result = []
 
198
    result.append("""<TABLE WIDTH="100\%" BORDER="0">""")
 
199
    result.append("""<TR><TD BGCOLOR="%s" VALIGN="CENTER">""" % bgcolor)
 
200
    result.append("""<H2>%s</H2>""" % text)
 
201
    result.append("""</TD></TR></TABLE>""")
 
202
    result.append('')
 
203
 
 
204
    return join(result, '\n')
 
205
 
 
206
 
 
207
def makeHtmlSubSection(text, bgcolor='#AAA0FF'):
 
208
    """Create HTML code for a subsection.
 
209
 
 
210
    This is usually a class or function name.
 
211
    """
 
212
    text = htmlescape(expandtabs(text))
 
213
    result = []
 
214
    result.append("""<TABLE WIDTH="100\%" BORDER="0">""")
 
215
    result.append("""<TR><TD BGCOLOR="%s" VALIGN="CENTER">""" % bgcolor)
 
216
    result.append("""<H3><TT><FONT SIZE="+2">%s</FONT></TT></H3>""" % text)
 
217
    result.append("""</TD></TR></TABLE>""")
 
218
    result.append('')
 
219
 
 
220
    return join(result, '\n')
 
221
 
 
222
 
 
223
def makeHtmlInlineImage(text):
 
224
    """Create HTML code for an inline image.
 
225
    """
 
226
 
 
227
    return """<IMG SRC="%s" ALT="%s">""" % (text, text)
 
228
 
 
229
 
 
230
####################################################################
 
231
#
 
232
# Core "standard" docpy classes
 
233
#
 
234
####################################################################
 
235
 
 
236
class PackageSkeleton0:
 
237
    """A class collecting 'interesting' information about a package."""
 
238
    pass # Not yet!
 
239
 
 
240
 
 
241
class ModuleSkeleton0:
 
242
    """A class collecting 'interesting' information about a module."""
 
243
 
 
244
    def __init__(self):
 
245
        # This is an ad-hoc, somewhat questionable 'data structure',
 
246
        # but for the time being it serves its purpose and is fairly
 
247
        # self-contained.
 
248
        self.module = {}
 
249
        self.functions = {}
 
250
        self.classes = {}
 
251
 
 
252
 
 
253
    # Might need more like this, later.
 
254
    def getModuleName(self):
 
255
        """Return the name of the module being treated."""
 
256
 
 
257
        return self.module['name']
 
258
 
 
259
 
 
260
    # These inspect methods all rely on the inspect module.
 
261
    def inspect(self, object):
 
262
        """Collect information about a given object."""
 
263
 
 
264
        self.moduleSpace = object
 
265
 
 
266
        # Very non-OO, left for later...
 
267
        if inspect.ismodule(object):
 
268
            self._inspectModule(object)
 
269
        elif inspect.isclass(object):
 
270
            self._inspectClass(object)
 
271
        elif inspect.ismethod(object):
 
272
            self._inspectMethod(object)
 
273
        elif inspect.isfunction(object):
 
274
            self._inspectFunction(object)
 
275
        elif inspect.isbuiltin(object):
 
276
            self._inspectBuiltin(object)
 
277
        else:
 
278
            msg = "Don't know how to document this kind of object."
 
279
            raise TypeError, msg
 
280
 
 
281
 
 
282
    def _inspectModule(self, object):
 
283
        """Collect information about a given module object."""
 
284
        name = object.__name__
 
285
 
 
286
        self.module['name'] = name
 
287
        if hasattr(object, '__version__'):
 
288
            self.module['version'] = object.__version__
 
289
 
 
290
        cadr = lambda list: list[1]
 
291
        modules = map(cadr, inspect.getmembers(object, inspect.ismodule))
 
292
 
 
293
        classes, cdict = [], {}
 
294
        for key, value in inspect.getmembers(object, inspect.isclass):
 
295
            if (inspect.getmodule(value) or object) is object:
 
296
                classes.append(value)
 
297
                cdict[key] = cdict[value] = '#' + key
 
298
 
 
299
        functions, fdict = [], {}
 
300
        for key, value in inspect.getmembers(object, inspect.isroutine):
 
301
            #if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
 
302
            functions.append(value)
 
303
            fdict[key] = '#-' + key
 
304
            if inspect.isfunction(value): fdict[value] = fdict[key]
 
305
 
 
306
        for c in classes:
 
307
            for base in c.__bases__:
 
308
                key, modname = base.__name__, base.__module__
 
309
                if modname != name and sys.modules.has_key(modname):
 
310
                    module = sys.modules[modname]
 
311
                    if hasattr(module, key) and getattr(module, key) is base:
 
312
                        if not cdict.has_key(key):
 
313
                            cdict[key] = cdict[base] = modname + '.txt#' + key
 
314
 
 
315
##        doc = getdoc(object) or 'No doc string.'
 
316
        doc = getdoc(object)
 
317
        self.module['doc'] = doc
 
318
 
 
319
        if modules:
 
320
            self.module['importedModules'] = map(lambda m:m.__name__, modules)
 
321
 
 
322
        if classes:
 
323
            for item in classes:
 
324
                self._inspectClass(item, fdict, cdict)
 
325
 
 
326
        if functions:
 
327
            for item in functions:
 
328
                self._inspectFunction(item, fdict, cdict)
 
329
 
 
330
 
 
331
    def _inspectClass(self, object, functions={}, classes={}):
 
332
        """Collect information about a given class object."""
 
333
 
 
334
        name = object.__name__
 
335
        bases = object.__bases__
 
336
        results = []
 
337
 
 
338
        if bases:
 
339
            parents = []
 
340
            for base in bases:
 
341
                parents.append(base)
 
342
 
 
343
        self.classes[name] = {}
 
344
        if bases:
 
345
            self.classes[name]['bases'] = parents
 
346
 
 
347
        methods, mdict = [], {}
 
348
        for key, value in inspect.getmembers(object, inspect.ismethod):
 
349
            methods.append(value)
 
350
            mdict[key] = mdict[value] = '#' + name + '-' + key
 
351
 
 
352
        if methods:
 
353
            if not self.classes[name].has_key('methods'):
 
354
                self.classes[name]['methods'] = {}
 
355
            for item in methods:
 
356
                self._inspectMethod(item, functions, classes, mdict, name)
 
357
 
 
358
##        doc = getdoc(object) or 'No doc string.'
 
359
        doc = getdoc(object)
 
360
        self.classes[name]['doc'] = doc
 
361
 
 
362
 
 
363
    def _inspectMethod(self, object, functions={}, classes={}, methods={}, clname=''):
 
364
        """Collect information about a given method object."""
 
365
 
 
366
        self._inspectFunction(object.im_func, functions, classes, methods, clname)
 
367
 
 
368
 
 
369
    def _inspectFunction(self, object, functions={}, classes={}, methods={}, clname=''):
 
370
        """Collect information about a given function object."""
 
371
        try:
 
372
            args, varargs, varkw, defaults = inspect.getargspec(object)
 
373
            argspec = inspect.formatargspec(
 
374
                args, varargs, varkw, defaults,
 
375
                defaultformat=defaultformat)
 
376
        except TypeError:
 
377
            argspec = '( ... )'
 
378
 
 
379
##        doc = getdoc(object) or 'No doc string.'
 
380
        doc = getdoc(object)
 
381
 
 
382
        if object.__name__ == '<lambda>':
 
383
            decl = [' lambda  ', argspec[1:-1]]
 
384
            # print '  %s' % decl
 
385
            # Do something with lambda functions as well...
 
386
            # ...
 
387
        else:
 
388
            decl = object.__name__
 
389
            if not clname:
 
390
                self.functions[object.__name__] = {'signature':argspec, 'doc':doc}
 
391
            else:
 
392
                theMethods = self.classes[clname]['methods']
 
393
                if not theMethods.has_key(object.__name__):
 
394
                    theMethods[object.__name__] = {}
 
395
 
 
396
                theMethod = theMethods[object.__name__]
 
397
                theMethod['signature'] = argspec
 
398
                theMethod['doc'] = doc
 
399
 
 
400
 
 
401
    def _inspectBuiltin(self, object):
 
402
        """Collect information about a given built-in."""
 
403
 
 
404
        print object.__name__ + '( ... )'
 
405
 
 
406
 
 
407
    def walk(self, formatter):
 
408
        """Call event methods in a visiting formatter."""
 
409
 
 
410
        s = self
 
411
        f = formatter
 
412
 
 
413
        # The order is fixed, but could be made flexible
 
414
        # with one more template method...
 
415
 
 
416
        # Module
 
417
        modName = s.module['name']
 
418
        modDoc = s.module['doc']
 
419
        imported = s.module.get('importedModules', [])
 
420
        imported.sort()
 
421
        # f.indentLevel = f.indentLevel + 1
 
422
        f.beginModule(modName, modDoc, imported)
 
423
 
 
424
        # Classes
 
425
        f.indentLevel = f.indentLevel + 1
 
426
        f.beginClasses(s.classes.keys())
 
427
        items = s.classes.items()
 
428
        items.sort()
 
429
        for k, v in items:
 
430
            cDoc = s.classes[k]['doc']
 
431
            bases = s.classes[k].get('bases', [])
 
432
            f.indentLevel = f.indentLevel + 1
 
433
            f.beginClass(k, cDoc, bases)
 
434
 
 
435
            # This if should move out of this method.
 
436
            if not s.classes[k].has_key('methods'):
 
437
                s.classes[k]['methods'] = {}
 
438
 
 
439
            # Methods
 
440
            #f.indentLevel = f.indentLevel + 1
 
441
            f.beginMethods(s.classes[k]['methods'].keys())
 
442
            items = s.classes[k]['methods'].items()
 
443
            items.sort()
 
444
            for m, v in items:
 
445
                mDoc = v['doc']
 
446
                sig = v['signature']
 
447
                f.indentLevel = f.indentLevel + 1
 
448
                f.beginMethod(m, mDoc, sig)
 
449
                f.indentLevel = f.indentLevel - 1
 
450
                f.endMethod(m, mDoc, sig)
 
451
 
 
452
            #f.indentLevel = f.indentLevel - 1
 
453
            f.endMethods(s.classes[k]['methods'].keys())
 
454
 
 
455
            f.indentLevel = f.indentLevel - 1
 
456
            f.endClass(k, cDoc, bases)
 
457
 
 
458
            # And what about attributes?!
 
459
 
 
460
        f.indentLevel = f.indentLevel - 1
 
461
        f.endClasses(s.classes.keys())
 
462
 
 
463
        # Functions
 
464
        f.indentLevel = f.indentLevel + 1
 
465
        f.beginFunctions(s.functions.keys())
 
466
        items = s.functions.items()
 
467
        items.sort()
 
468
        for k, v in items:
 
469
            doc = v['doc']
 
470
            sig = v['signature']
 
471
            f.indentLevel = f.indentLevel + 1
 
472
            f.beginFunction(k, doc, sig)
 
473
            f.indentLevel = f.indentLevel - 1
 
474
            f.endFunction(k, doc, sig)
 
475
        f.indentLevel = f.indentLevel - 1
 
476
        f.endFunctions(s.functions.keys())
 
477
 
 
478
        #f.indentLevel = f.indentLevel - 1
 
479
        f.endModule(modName, modDoc, imported)
 
480
 
 
481
        # Constants?!
 
482
 
 
483
 
 
484
####################################################################
 
485
#
 
486
# Core "standard" docpy document builders
 
487
#
 
488
####################################################################
 
489
 
 
490
class DocBuilder0:
 
491
    """An abstract class to document the skeleton of a Python module.
 
492
 
 
493
    Instances take a skeleton instance s and call their s.walk()
 
494
    method. The skeleton, in turn, will walk over its tree structure
 
495
    while generating events and calling equivalent methods from a
 
496
    specific interface (begin/end methods).
 
497
    """
 
498
 
 
499
    fileSuffix = None
 
500
 
 
501
    def __init__(self, skeleton=None):
 
502
        self.skeleton = skeleton
 
503
        self.packageName = None
 
504
        self.indentLevel = 0
 
505
 
 
506
 
 
507
    def write(self, skeleton=None):
 
508
        if skeleton:
 
509
            self.skeleton = skeleton
 
510
        self.skeleton.walk(self)
 
511
 
 
512
 
 
513
    # Event-method API, called by associated skeleton instances.
 
514
    # In fact, these should raise a NotImplementedError, but for now we
 
515
    # just don't do anything here.
 
516
 
 
517
    # The following four methods are *not* called by skeletons!
 
518
    def begin(self, name='', typ=''): pass
 
519
    def end(self): pass
 
520
 
 
521
    # Methods for packaging should move into a future PackageSkeleton...
 
522
    def beginPackage(self, name):
 
523
        self.packageName = name
 
524
 
 
525
    def endPackage(self, name):
 
526
        pass
 
527
 
 
528
    # Only this subset is really called by associated skeleton instances.
 
529
 
 
530
    def beginModule(self, name, doc, imported): pass
 
531
    def endModule(self, name, doc, imported): pass
 
532
 
 
533
    def beginClasses(self, names): pass
 
534
    def endClasses(self, names): pass
 
535
 
 
536
    def beginClass(self, name, doc, bases): pass
 
537
    def endClass(self, name, doc, bases): pass
 
538
 
 
539
    def beginMethods(self, names): pass
 
540
    def endMethods(self, names): pass
 
541
 
 
542
    def beginMethod(self, name, doc, sig): pass
 
543
    def endMethod(self, name, doc, sig): pass
 
544
 
 
545
    def beginFunctions(self, names): pass
 
546
    def endFunctions(self, names): pass
 
547
 
 
548
    def beginFunction(self, name, doc, sig): pass
 
549
    def endFunction(self, name, doc, sig): pass
 
550
 
 
551
 
 
552
class AsciiDocBuilder0(DocBuilder0):
 
553
    """Document the skeleton of a Python module in ASCII format.
 
554
 
 
555
    The output will be an ASCII file with nested lines representing
 
556
    the hiearchical module structure.
 
557
 
 
558
    Currently, no doc strings are listed.
 
559
    """
 
560
 
 
561
    fileSuffix = '.txt'
 
562
    outLines = []
 
563
    indentLabel = '  '
 
564
 
 
565
    def end(self):
 
566
        # This if should move into DocBuilder0...
 
567
        if self.packageName:
 
568
            self.outPath = self.packageName + self.fileSuffix
 
569
        elif self.skeleton:
 
570
            self.outPath = self.skeleton.getModuleName() + self.fileSuffix
 
571
        else:
 
572
            self.outPath = ''
 
573
 
 
574
        if self.outPath:
 
575
            file = open(self.outPath, 'w')
 
576
            for line in self.outLines:
 
577
                file.write(line + '\n')
 
578
            file.close()
 
579
 
 
580
 
 
581
    def beginPackage(self, name):
 
582
        DocBuilder0.beginPackage(self, name)
 
583
        lev, label = self.indentLevel, self.indentLabel
 
584
        self.outLines.append('%sPackage: %s' % (lev*label, name))
 
585
        self.outLines.append('')
 
586
 
 
587
 
 
588
    def beginModule(self, name, doc, imported):
 
589
        append = self.outLines.append
 
590
        lev, label = self.indentLevel, self.indentLabel
 
591
        self.outLines.append('%sModule: %s' % (lev*label, name))
 
592
##        self.outLines.append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
 
593
        append('')
 
594
 
 
595
        if imported:
 
596
            self.outLines.append('%sImported' % ((lev+1)*label))
 
597
            append('')
 
598
            for m in imported:
 
599
                self.outLines.append('%s%s' % ((lev+2)*label, m))
 
600
            append('')
 
601
 
 
602
 
 
603
    def beginClasses(self, names):
 
604
        if names:
 
605
            lev, label = self.indentLevel, self.indentLabel
 
606
            self.outLines.append('%sClasses' % (lev*label))
 
607
            self.outLines.append('')
 
608
 
 
609
 
 
610
    def beginClass(self, name, doc, bases):
 
611
        append = self.outLines.append
 
612
        lev, label = self.indentLevel, self.indentLabel
 
613
 
 
614
        if bases:
 
615
            bases = map(lambda b:b.__name__, bases) # hack
 
616
            append('%s%s(%s)' % (lev*label, name, join(bases, ', ')))
 
617
        else:
 
618
            append('%s%s' % (lev*label, name))
 
619
        return
 
620
 
 
621
##        append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
 
622
        self.outLines.append('')
 
623
 
 
624
 
 
625
    def endClass(self, name, doc, bases):
 
626
        self.outLines.append('')
 
627
 
 
628
 
 
629
    def beginMethod(self, name, doc, sig):
 
630
        append = self.outLines.append
 
631
        lev, label = self.indentLevel, self.indentLabel
 
632
        append('%s%s%s' % (lev*label, name, sig))
 
633
##        append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
 
634
##        append('')
 
635
 
 
636
 
 
637
    def beginFunctions(self, names):
 
638
        if names:
 
639
            lev, label = self.indentLevel, self.indentLabel
 
640
            self.outLines.append('%sFunctions' % (lev*label))
 
641
            self.outLines.append('')
 
642
 
 
643
 
 
644
    def endFunctions(self, names):
 
645
        self.outLines.append('')
 
646
 
 
647
 
 
648
    def beginFunction(self, name, doc, sig):
 
649
        append = self.outLines.append
 
650
        lev, label = self.indentLevel, self.indentLabel
 
651
        self.outLines.append('%s%s%s' % (lev*label, name, sig))
 
652
##        append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
 
653
##        append('')
 
654
 
 
655
 
 
656
class HtmlDocBuilder0(DocBuilder0):
 
657
    "A class to write the skeleton of a Python source in HTML format."
 
658
 
 
659
    fileSuffix = '.html'
 
660
    outLines = []
 
661
 
 
662
    def begin(self, name='', typ=''):
 
663
        self.outLines.append("""<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">""")
 
664
        self.outLines.append("""<html>""")
 
665
 
 
666
 
 
667
    def end(self):
 
668
        if self.packageName:
 
669
            self.outPath = self.packageName + self.fileSuffix
 
670
        elif self.skeleton:
 
671
            self.outPath = self.skeleton.getModuleName() + self.fileSuffix
 
672
        else:
 
673
            self.outPath = ''
 
674
 
 
675
        if self.outPath:
 
676
            file = open(self.outPath, 'w')
 
677
            self.outLines.append('</body></html>')
 
678
            for line in self.outLines:
 
679
                file.write(line + '\n')
 
680
            file.close()
 
681
 
 
682
 
 
683
    def beginPackage(self, name):
 
684
        DocBuilder0.beginPackage(self, name)
 
685
 
 
686
        self.outLines.append("""<title>%s</title>""" % name)
 
687
        self.outLines.append("""<body bgcolor="#ffffff">""")
 
688
        self.outLines.append("""<H1>%s</H1>""" % name)
 
689
        self.outLines.append('')
 
690
 
 
691
 
 
692
    def beginModule(self, name, doc, imported):
 
693
        if not self.packageName:
 
694
            self.outLines.append("""<title>%s</title>""" % name)
 
695
            self.outLines.append("""<body bgcolor="#ffffff">""")
 
696
 
 
697
        self.outLines.append("""<H1>%s</H1>""" % name)
 
698
        self.outLines.append('')
 
699
        for line in split(doc, '\n'):
 
700
            self.outLines.append("""<FONT SIZE="-1">%s</FONT>""" % htmlescape(line))
 
701
            self.outLines.append('<BR>')
 
702
        self.outLines.append('')
 
703
 
 
704
        if imported:
 
705
            self.outLines.append(makeHtmlSection('Imported Modules'))
 
706
            self.outLines.append("""<ul>""")
 
707
            for m in imported:
 
708
                self.outLines.append("""<li>%s</li>""" % m)
 
709
            self.outLines.append("""</ul>""")
 
710
 
 
711
 
 
712
    def beginClasses(self, names):
 
713
        self.outLines.append(makeHtmlSection('Classes'))
 
714
 
 
715
 
 
716
    def beginClass(self, name, doc, bases):
 
717
        DocBuilder0.beginClass(self, name, doc, bases)
 
718
 
 
719
##        # Keep an eye on the base classes.
 
720
##        self.currentBaseClasses = bases
 
721
 
 
722
        if bases:
 
723
            bases = map(lambda b:b.__name__, bases) # hack
 
724
            self.outLines.append(makeHtmlSubSection('%s(%s)' % (name, join(bases, ', '))))
 
725
        else:
 
726
            self.outLines.append(makeHtmlSubSection('%s' % name))
 
727
        for line in split(doc, '\n'):
 
728
            self.outLines.append("""<FONT SIZE="-1">%s</FONT>""" % htmlescape(line))
 
729
            self.outLines.append('<BR>')
 
730
 
 
731
        self.outLines.append('')
 
732
 
 
733
 
 
734
    def beginMethods(self, names):
 
735
        pass
 
736
##        if names:
 
737
##            self.outLines.append('<H3>Method Interface</H3>')
 
738
##            self.outLines.append('')
 
739
 
 
740
 
 
741
    def beginMethod(self, name, doc, sig):
 
742
        self.beginFunction(name, doc, sig)
 
743
 
 
744
 
 
745
    def beginFunctions(self, names):
 
746
        self.outLines.append(makeHtmlSection('Functions'))
 
747
 
 
748
 
 
749
    def beginFunction(self, name, doc, sig):
 
750
        append = self.outLines.append
 
751
        append("""<DL><DL><DT><TT><STRONG>%s</STRONG>%s</TT></DT>""" % (name, sig))
 
752
        append('')
 
753
        for line in split(doc, '\n'):
 
754
            append("""<DD><FONT SIZE="-1">%s</FONT></DD>""" % htmlescape(line))
 
755
            append('<BR>')
 
756
        append('</DL></DL>')
 
757
        append('')
 
758
 
 
759
 
 
760
class PdfDocBuilder0(DocBuilder0):
 
761
    "Document the skeleton of a Python module in PDF format."
 
762
 
 
763
    fileSuffix = '.pdf'
 
764
 
 
765
    def makeHeadingStyle(self, level, typ=None, doc=''):
 
766
        "Make a heading style for different types of module content."
 
767
 
 
768
        if typ in ('package', 'module', 'class'):
 
769
            style = ParagraphStyle(name='Heading'+str(level),
 
770
                                      fontName = 'Courier-Bold',
 
771
                                      fontSize=14,
 
772
                                      leading=18,
 
773
                                      spaceBefore=12,
 
774
                                      spaceAfter=6)
 
775
        elif typ in ('method', 'function'):
 
776
            if doc:
 
777
                style = ParagraphStyle(name='Heading'+str(level),
 
778
                                          fontName = 'Courier-Bold',
 
779
                                          fontSize=12,
 
780
                                          leading=18,
 
781
                                          firstLineIndent=-18,
 
782
                                          leftIndent=36,
 
783
                                          spaceBefore=0,
 
784
                                          spaceAfter=-3)
 
785
            else:
 
786
                style = ParagraphStyle(name='Heading'+str(level),
 
787
                                          fontName = 'Courier-Bold',
 
788
                                          fontSize=12,
 
789
                                          leading=18,
 
790
                                          firstLineIndent=-18,
 
791
                                          leftIndent=36,
 
792
                                          spaceBefore=0,
 
793
                                          spaceAfter=0)
 
794
 
 
795
        else:
 
796
            style = ParagraphStyle(name='Heading'+str(level),
 
797
                                      fontName = 'Times-Bold',
 
798
                                      fontSize=14,
 
799
                                      leading=18,
 
800
                                      spaceBefore=12,
 
801
                                      spaceAfter=6)
 
802
 
 
803
        return style
 
804
 
 
805
 
 
806
    def begin(self, name='', typ=''):
 
807
        styleSheet = getSampleStyleSheet()
 
808
        self.code = styleSheet['Code']
 
809
        self.bt = styleSheet['BodyText']
 
810
        self.story = []
 
811
 
 
812
        # Cover page
 
813
        t = time.gmtime(time.time())
 
814
        timeString = time.strftime("%Y-%m-%d %H:%M", t)
 
815
        self.story.append(Paragraph('<font size=18>Documentation for %s "%s"</font>' % (typ, name), self.bt))
 
816
        self.story.append(Paragraph('<font size=18>Generated by: docpy.py version %s</font>' %  __version__, self.bt))
 
817
        self.story.append(Paragraph('<font size=18>Date generated: %s</font>' % timeString, self.bt))
 
818
        self.story.append(Paragraph('<font size=18>Format: PDF</font>', self.bt))
 
819
        self.story.append(PageBreak())
 
820
 
 
821
        # Table of contents
 
822
        toc = TableOfContents()
 
823
        self.story.append(toc)
 
824
        self.story.append(PageBreak())
 
825
 
 
826
 
 
827
    def end(self):
 
828
        if self.outPath is not None:
 
829
            pass
 
830
        elif self.packageName:
 
831
            self.outPath = self.packageName + self.fileSuffix
 
832
        elif self.skeleton:
 
833
            self.outPath = self.skeleton.getModuleName() + self.fileSuffix
 
834
        else:
 
835
            self.outPath = ''
 
836
        print 'output path is %s' % self.outPath
 
837
        if self.outPath:
 
838
            doc = MyTemplate(self.outPath)
 
839
            doc.multiBuild(self.story)
 
840
 
 
841
 
 
842
    def beginPackage(self, name):
 
843
        DocBuilder0.beginPackage(self, name)
 
844
        story = self.story
 
845
        story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'package')))
 
846
 
 
847
 
 
848
    def beginModule(self, name, doc, imported):
 
849
        story = self.story
 
850
        bt = self.bt
 
851
        story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'module')))
 
852
        if doc:
 
853
            story.append(XPreformatted(htmlescape(doc), bt))
 
854
            story.append(XPreformatted('', bt))
 
855
 
 
856
        if imported:
 
857
            story.append(Paragraph('Imported modules', self.makeHeadingStyle(self.indentLevel + 1)))
 
858
            for m in imported:
 
859
                p = Paragraph('<bullet>\201</bullet> %s' % m, bt)
 
860
                p.style.bulletIndent = 10
 
861
                p.style.leftIndent = 18
 
862
                story.append(p)
 
863
 
 
864
 
 
865
    def endModule(self, name, doc, imported):
 
866
        DocBuilder0.endModule(self, name, doc, imported)
 
867
        self.story.append(PageBreak())
 
868
 
 
869
 
 
870
    def beginClasses(self, names):
 
871
        self.story.append(Paragraph('Classes', self.makeHeadingStyle(self.indentLevel)))
 
872
 
 
873
 
 
874
    def beginClass(self, name, doc, bases):
 
875
        bt = self.bt
 
876
        story = self.story
 
877
        if bases:
 
878
            bases = map(lambda b:b.__name__, bases) # hack
 
879
            story.append(Paragraph('%s(%s)' % (name, join(bases, ', ')), self.makeHeadingStyle(self.indentLevel, 'class')))
 
880
        else:
 
881
            story.append(Paragraph(name, self.makeHeadingStyle(self.indentLevel, 'class')))
 
882
 
 
883
        if doc:
 
884
            story.append(XPreformatted(htmlescape(doc), bt))
 
885
            story.append(XPreformatted('', bt))
 
886
 
 
887
 
 
888
    def beginMethod(self, name, doc, sig):
 
889
        bt = self.bt
 
890
        story = self.story
 
891
        story.append(Paragraph(name+sig, self.makeHeadingStyle(self.indentLevel, 'method', doc)))
 
892
        if doc:
 
893
            story.append(XPreformatted(htmlescape(doc), bt))
 
894
            story.append(XPreformatted('', bt))
 
895
 
 
896
 
 
897
    def beginFunctions(self, names):
 
898
        if names:
 
899
            self.story.append(Paragraph('Functions', self.makeHeadingStyle(self.indentLevel)))
 
900
 
 
901
 
 
902
    def beginFunction(self, name, doc, sig):
 
903
        bt = self.bt
 
904
        story = self.story
 
905
        story.append(Paragraph(name+sig, self.makeHeadingStyle(self.indentLevel, 'function')))
 
906
        if doc:
 
907
            story.append(XPreformatted(htmlescape(doc), bt))
 
908
            story.append(XPreformatted('', bt))
 
909
 
 
910
 
 
911
class UmlPdfDocBuilder0(PdfDocBuilder0):
 
912
    "Document the skeleton of a Python module with UML class diagrams."
 
913
 
 
914
    fileSuffix = '.pdf'
 
915
 
 
916
    def begin(self, name='', typ=''):
 
917
        styleSheet = getSampleStyleSheet()
 
918
        self.h1 = styleSheet['Heading1']
 
919
        self.h2 = styleSheet['Heading2']
 
920
        self.h3 = styleSheet['Heading3']
 
921
        self.code = styleSheet['Code']
 
922
        self.bt = styleSheet['BodyText']
 
923
        self.story = []
 
924
        self.classCompartment = ''
 
925
        self.methodCompartment = []
 
926
 
 
927
 
 
928
    def beginModule(self, name, doc, imported):
 
929
        story = self.story
 
930
        h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt
 
931
        styleSheet = getSampleStyleSheet()
 
932
        bt1 = styleSheet['BodyText']
 
933
 
 
934
        story.append(Paragraph(name, h1))
 
935
        story.append(XPreformatted(doc, bt1))
 
936
 
 
937
        if imported:
 
938
            story.append(Paragraph('Imported modules', self.makeHeadingStyle(self.indentLevel + 1)))
 
939
            for m in imported:
 
940
                p = Paragraph('<bullet>\201</bullet> %s' % m, bt1)
 
941
                p.style.bulletIndent = 10
 
942
                p.style.leftIndent = 18
 
943
                story.append(p)
 
944
 
 
945
 
 
946
    def endModule(self, name, doc, imported):
 
947
        self.story.append(PageBreak())
 
948
        PdfDocBuilder0.endModule(self, name, doc, imported)
 
949
 
 
950
 
 
951
    def beginClasses(self, names):
 
952
        h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt
 
953
        if names:
 
954
            self.story.append(Paragraph('Classes', h2))
 
955
 
 
956
 
 
957
    def beginClass(self, name, doc, bases):
 
958
        self.classCompartment = ''
 
959
        self.methodCompartment = []
 
960
 
 
961
        if bases:
 
962
            bases = map(lambda b:b.__name__, bases) # hack
 
963
            self.classCompartment = '%s(%s)' % (name, join(bases, ', '))
 
964
        else:
 
965
            self.classCompartment = name
 
966
 
 
967
 
 
968
    def endClass(self, name, doc, bases):
 
969
        h1, h2, h3, bt, code = self.h1, self.h2, self.h3, self.bt, self.code
 
970
        styleSheet = getSampleStyleSheet()
 
971
        bt1 = styleSheet['BodyText']
 
972
        story = self.story
 
973
 
 
974
        # Use only the first line of the class' doc string --
 
975
        # no matter how long! (Do the same later for methods)
 
976
        classDoc = reduceDocStringLength(doc)
 
977
 
 
978
        tsa = tableStyleAttributes = []
 
979
 
 
980
        # Make table with class and method rows
 
981
        # and add it to the story.
 
982
        p = Paragraph('<b>%s</b>' % self.classCompartment, bt)
 
983
        p.style.alignment = TA_CENTER
 
984
        rows = [(p,)]
 
985
        # No doc strings, now...
 
986
        # rows = rows + [(Paragraph('<i>%s</i>' % classDoc, bt1),)]
 
987
        lenRows = len(rows)
 
988
        tsa.append(('BOX', (0,0), (-1,lenRows-1), 0.25, colors.black))
 
989
        for name, doc, sig in self.methodCompartment:
 
990
            nameAndSig = Paragraph('<b>%s</b>%s' % (name, sig), bt1)
 
991
            rows.append((nameAndSig,))
 
992
            # No doc strings, now...
 
993
            # docStr = Paragraph('<i>%s</i>' % reduceDocStringLength(doc), bt1)
 
994
            # rows.append((docStr,))
 
995
        tsa.append(('BOX', (0,lenRows), (-1,-1), 0.25, colors.black))
 
996
        t = Table(rows, (12*cm,))
 
997
        tableStyle = TableStyle(tableStyleAttributes)
 
998
        t.setStyle(tableStyle)
 
999
        self.story.append(t)
 
1000
        self.story.append(Spacer(1*cm, 1*cm))
 
1001
 
 
1002
 
 
1003
    def beginMethod(self, name, doc, sig):
 
1004
        self.methodCompartment.append((name, doc, sig))
 
1005
 
 
1006
 
 
1007
    def beginFunctions(self, names):
 
1008
        h1, h2, h3, bt = self.h1, self.h2, self.h3, self.bt
 
1009
        if names:
 
1010
            self.story.append(Paragraph('Functions', h2))
 
1011
        self.classCompartment = chr(171) + ' Module-Level Functions ' + chr(187)
 
1012
        self.methodCompartment = []
 
1013
 
 
1014
 
 
1015
    def beginFunction(self, name, doc, sig):
 
1016
        self.methodCompartment.append((name, doc, sig))
 
1017
 
 
1018
 
 
1019
    def endFunctions(self, names):
 
1020
        h1, h2, h3, bt, code = self.h1, self.h2, self.h3, self.bt, self.code
 
1021
        styleSheet = getSampleStyleSheet()
 
1022
        bt1 = styleSheet['BodyText']
 
1023
        story = self.story
 
1024
        if not names:
 
1025
            return
 
1026
 
 
1027
        tsa = tableStyleAttributes = []
 
1028
 
 
1029
        # Make table with class and method rows
 
1030
        # and add it to the story.
 
1031
        p = Paragraph('<b>%s</b>' % self.classCompartment, bt)
 
1032
        p.style.alignment = TA_CENTER
 
1033
        rows = [(p,)]
 
1034
        lenRows = len(rows)
 
1035
        tsa.append(('BOX', (0,0), (-1,lenRows-1), 0.25, colors.black))
 
1036
        for name, doc, sig in self.methodCompartment:
 
1037
            nameAndSig = Paragraph('<b>%s</b>%s' % (name, sig), bt1)
 
1038
            rows.append((nameAndSig,))
 
1039
            # No doc strings, now...
 
1040
            # docStr = Paragraph('<i>%s</i>' % reduceDocStringLength(doc), bt1)
 
1041
            # rows.append((docStr,))
 
1042
        tsa.append(('BOX', (0,lenRows), (-1,-1), 0.25, colors.black))
 
1043
        t = Table(rows, (12*cm,))
 
1044
        tableStyle = TableStyle(tableStyleAttributes)
 
1045
        t.setStyle(tableStyle)
 
1046
        self.story.append(t)
 
1047
        self.story.append(Spacer(1*cm, 1*cm))
 
1048
 
 
1049
 
 
1050
####################################################################
 
1051
#
 
1052
# Main
 
1053
#
 
1054
####################################################################
 
1055
 
 
1056
def printUsage():
 
1057
    """docpy.py - Automated documentation for Python source code.
 
1058
 
 
1059
Usage: python docpy.py [options]
 
1060
 
 
1061
    [options]
 
1062
        -h          Print this help message.
 
1063
 
 
1064
        -f name     Use the document builder indicated by 'name',
 
1065
                    e.g. Ascii, Html, Pdf (default), UmlPdf.
 
1066
 
 
1067
        -m module   Generate document for module named 'module'
 
1068
                    (default is 'docpy').
 
1069
                    'module' may follow any of these forms:
 
1070
                        - docpy.py
 
1071
                        - docpy
 
1072
                        - c:\\test\\docpy
 
1073
                    and can be any of these:
 
1074
                        - standard Python modules
 
1075
                        - modules in the Python search path
 
1076
                        - modules in the current directory
 
1077
 
 
1078
        -p package  Generate document for package named 'package'.
 
1079
                    'package' may follow any of these forms:
 
1080
                        - reportlab
 
1081
                        - reportlab.platypus
 
1082
                        - c:\\test\\reportlab
 
1083
                    and can be any of these:
 
1084
                        - standard Python packages (?)
 
1085
                        - packages in the Python search path
 
1086
                        - packages in the current directory
 
1087
 
 
1088
        -s          Silent mode (default is unset).
 
1089
 
 
1090
Examples:
 
1091
 
 
1092
    python docpy.py -h
 
1093
    python docpy.py -m docpy.py -f Ascii
 
1094
    python docpy.py -m string -f Html
 
1095
    python docpy.py -m signsandsymbols.py -f Pdf
 
1096
    python docpy.py -p reportlab.platypus -f UmlPdf
 
1097
    python docpy.py -p reportlab.lib -s -f UmlPdf
 
1098
"""
 
1099
 
 
1100
 
 
1101
def documentModule0(pathOrName, builder, opts={}):
 
1102
    """Generate documentation for one Python file in some format.
 
1103
 
 
1104
    This handles Python standard modules like string, custom modules
 
1105
    on the Python search path like e.g. docpy as well as modules
 
1106
    specified with their full path like C:/tmp/junk.py.
 
1107
 
 
1108
    The doc file will always be saved in the current directory with
 
1109
    a basename equal to that of the module, e.g. docpy.
 
1110
    """
 
1111
 
 
1112
    cwd = os.getcwd()
 
1113
 
 
1114
    # Append directory to Python search path if we get one.
 
1115
    dirName = os.path.dirname(pathOrName)
 
1116
    if dirName:
 
1117
        sys.path.append(dirName)
 
1118
 
 
1119
    # Remove .py extension from module name.
 
1120
    if pathOrName[-3:] == '.py':
 
1121
        modname = pathOrName[:-3]
 
1122
    else:
 
1123
        modname = pathOrName
 
1124
 
 
1125
    # Remove directory paths from module name.
 
1126
    if dirName:
 
1127
        modname = os.path.basename(modname)
 
1128
 
 
1129
    # Load the module.
 
1130
    try:
 
1131
        module = __import__(modname)
 
1132
    except:
 
1133
        print 'Failed to import %s.' % modname
 
1134
        os.chdir(cwd)
 
1135
        return
 
1136
 
 
1137
    # Do the real documentation work.
 
1138
    s = ModuleSkeleton0()
 
1139
    s.inspect(module)
 
1140
    builder.write(s)
 
1141
 
 
1142
    # Remove appended directory from Python search path if we got one.
 
1143
    if dirName:
 
1144
        del sys.path[-1]
 
1145
 
 
1146
    os.chdir(cwd)
 
1147
 
 
1148
 
 
1149
def _packageWalkCallback((builder, opts), dirPath, files):
 
1150
    "A callback function used when waking over a package tree."
 
1151
 
 
1152
    # Skip __init__ files.
 
1153
    files = filter(lambda f:f != '__init__.py', files)
 
1154
 
 
1155
    files = filter(lambda f:f[-3:] == '.py', files)
 
1156
    for f in files:
 
1157
        path = os.path.join(dirPath, f)
 
1158
        if not opts.get('isSilent', 0):
 
1159
            print path
 
1160
        builder.indentLevel = builder.indentLevel + 1
 
1161
        documentModule0(path, builder)
 
1162
        builder.indentLevel = builder.indentLevel - 1
 
1163
 
 
1164
 
 
1165
def documentPackage0(pathOrName, builder, opts={}):
 
1166
    """Generate documentation for one Python package in some format.
 
1167
 
 
1168
    'pathOrName' can be either a filesystem path leading to a Python
 
1169
    package or package name whose path will be resolved by importing
 
1170
    the top-level module.
 
1171
 
 
1172
    The doc file will always be saved in the current directory with
 
1173
    a basename equal to that of the package, e.g. reportlab.lib.
 
1174
    """
 
1175
 
 
1176
    # Did we get a package path with OS-dependant seperators...?
 
1177
    if os.sep in pathOrName:
 
1178
        path = pathOrName
 
1179
        name = os.path.splitext(os.path.basename(path))[0]
 
1180
    # ... or rather a package name?
 
1181
    else:
 
1182
        name = pathOrName
 
1183
        package = __import__(name)
 
1184
        # Some special care needed for dotted names.
 
1185
        if '.' in name:
 
1186
            subname = 'package' + name[find(name, '.'):]
 
1187
            package = eval(subname)
 
1188
        path = os.path.dirname(package.__file__)
 
1189
 
 
1190
    cwd = os.getcwd()
 
1191
    builder.beginPackage(name)
 
1192
    os.path.walk(path, _packageWalkCallback, (builder, opts))
 
1193
    builder.endPackage(name)
 
1194
    os.chdir(cwd)
 
1195
 
 
1196
 
 
1197
def main():
 
1198
    "Handle command-line options and trigger corresponding action."
 
1199
 
 
1200
    opts, args = getopt.getopt(sys.argv[1:], 'hsf:m:p:')
 
1201
 
 
1202
    # Make an options dictionary that is easier to use.
 
1203
    optsDict = {}
 
1204
    for k, v in opts:
 
1205
        optsDict[k] = v
 
1206
    hasOpt = optsDict.has_key
 
1207
 
 
1208
    # On -h print usage and exit immediately.
 
1209
    if hasOpt('-h'):
 
1210
        print printUsage.__doc__
 
1211
        sys.exit(0)
 
1212
 
 
1213
    # On -s set silent mode.
 
1214
    isSilent = hasOpt('-s')
 
1215
 
 
1216
    # On -f set the appropriate DocBuilder to use or a default one.
 
1217
    builderClassName = optsDict.get('-f', 'Pdf') + 'DocBuilder0'
 
1218
    builder = eval(builderClassName + '()')
 
1219
 
 
1220
    # Set default module or package to document.
 
1221
    if not hasOpt('-p') and not hasOpt('-m'):
 
1222
        optsDict['-m'] = 'docpy'
 
1223
 
 
1224
    # Save a few options for further use.
 
1225
    options = {'isSilent':isSilent}
 
1226
 
 
1227
    # Now call the real documentation functions.
 
1228
    if hasOpt('-m'):
 
1229
        nameOrPath = optsDict['-m']
 
1230
        if not isSilent:
 
1231
            print "Generating documentation for module %s..." % nameOrPath
 
1232
        builder.begin(name=nameOrPath, typ='module')
 
1233
        documentModule0(nameOrPath, builder, options)
 
1234
    elif hasOpt('-p'):
 
1235
        nameOrPath = optsDict['-p']
 
1236
        if not isSilent:
 
1237
            print "Generating documentation for package %s..." % nameOrPath
 
1238
        builder.begin(name=nameOrPath, typ='package')
 
1239
        documentPackage0(nameOrPath, builder, options)
 
1240
    builder.end()
 
1241
 
 
1242
    if not isSilent:
 
1243
        print "Saved %s." % builder.outPath
 
1244
 
 
1245
 
 
1246
if __name__ == '__main__':
 
1247
    main()