root/livinglogic.python.xist/src/ll/xist/ns/doc.py @ 3168:5cdf156ca704

Revision 3168:5cdf156ca704, 38.8 KB (checked in by Walter Doerwald <walter@…>, 12 years ago)

Add a proper ReST namespace with transformation to doc. Shorten name of doc elements.

Add real text roles for :class:foo etc. Reused doc.code HTML conversion code.

Line 
1# -*- coding: utf-8 -*-
2
3## Copyright 1999-2008 by LivingLogic AG, Bayreuth/Germany
4## Copyright 1999-2008 by Walter Dörwald
5##
6## All Rights Reserved
7##
8## See xist/__init__.py for the license
9
10
11"""
12<p>This namespace module provides classes that can be used for generating
13documentation (in &html;, DocBook and XSL-FO).</p>
14"""
15
16
17from __future__ import with_statement
18
19import sys, types, inspect, textwrap, warnings, operator
20
21import ll
22from ll.xist import xsc, parsers, sims, xfind
23from ll.xist.ns import html, docbook, fo, specials, xml, abbr as abbr_
24
25
26__docformat__ = "xist"
27
28
29xmlns = "http://xmlns.livinglogic.de/xist/ns/doc"
30
31
32def _getmodulename(thing):
33    module = inspect.getmodule(thing)
34    if module is None:
35        return "???"
36    else:
37        return module.__name__
38
39
40def getdoc(thing, format):
41    if thing.__doc__ is None:
42        return xsc.Null
43
44    # Remove indentation
45    lines = textwrap.dedent(thing.__doc__).split("\n")
46
47    # remove empty lines
48    while lines and not lines[0]:
49        del lines[0]
50    while lines and not lines[-1]:
51        del lines[-1]
52
53    text = "\n".join(lines)
54
55    if format.lower() == "plaintext":
56        return xsc.Text(text)
57    elif format.lower() == "restructuredtext":
58        from ll.xist.ns import rest
59        return rest.fromstring(text).conv()
60    elif format.lower() == "xist":
61        if inspect.ismethod(thing):
62            base = "METHOD-DOCSTRING(%s.%s.%s)" % (_getmodulename(thing), thing.im_class.__name__, thing.__name__)
63        elif isinstance(thing, property):
64            base = "PROPERTY-DOCSTRING(%s.%s)" % (_getmodulename(thing), "unknown")
65        elif inspect.isfunction(thing):
66            base = "FUNCTION-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__)
67        elif inspect.isclass(thing):
68            base = "CLASS-DOCSTRING(%s.%s)" % (_getmodulename(thing), thing.__name__)
69        elif inspect.ismodule(thing):
70            base = "MODULE-DOCSTRING(%s)" % _getmodulename(thing)
71        else:
72            base = "DOCSTRING"
73        node = parsers.parsestring(text, base=base, prefixes=xsc.docprefixes())
74        if not node[p]: # optimization: one paragraph docstrings don't need a <p> element.
75            node = p(node)
76
77        if inspect.ismethod(thing):
78            # Use the original method instead of the decorator
79            realthing = thing
80            while hasattr(realthing, "__wrapped__"):
81                realthing = realthing.__wrapped__
82            for ref in node.walknode(pyref):
83                if u"module" not in ref.attrs:
84                    ref[u"module"] = _getmodulename(realthing)
85                    if u"class_" not in ref.attrs:
86                        ref[u"class_"] = thing.im_class.__name__
87                        if u"method" not in ref.attrs:
88                            ref[u"method"] = thing.__name__
89        elif inspect.isfunction(thing):
90            # Use the original method instead of the decorator
91            while hasattr(thing, "__wrapped__"):
92                thing = thing.__wrapped__
93            for ref in node.walknode(pyref):
94                if u"module" not in ref.attrs:
95                    ref[u"module"] = _getmodulename(thing)
96        elif inspect.isclass(thing):
97            for ref in node.walknode(pyref):
98                if u"module" not in ref.attrs:
99                    ref[u"module"] = _getmodulename(thing)
100                    if u"class_" not in ref.attrs:
101                        ref[u"class_"] = thing.__name__
102        elif inspect.ismodule(thing):
103            for ref in node.walknode(pyref):
104                if u"module" not in ref.attrs:
105                    ref[u"module"] = thing.__name__
106        return node
107    else:
108        raise ValueError("unsupported __docformat__ %r" % format)
109
110
111def getsourceline(obj):
112    if isinstance(obj, property):
113        pos = 999999999
114        if obj.fget is not None:
115            pos = min(pos, obj.fget.func_code.co_firstlineno)
116        if obj.fset is not None:
117            pos = min(pos, obj.fset.func_code.co_firstlineno)
118        if obj.fdel is not None:
119            pos = min(pos, obj.fdel.func_code.co_firstlineno)
120    else:
121        while hasattr(obj, "__wrapped__"):
122            obj = obj.__wrapped__
123        try: # FIXME: Python SF bug #1224621
124            pos = inspect.getsourcelines(obj)[-1]
125        except IndentationError:
126            pos = 42
127    return pos
128
129
130def _namekey(obj, name):
131    return (getsourceline(obj), name or obj.__name__)
132
133
134def _codeheader(thing, name, type):
135    (args, varargs, varkw, defaults) = inspect.getargspec(thing)
136    sig = xsc.Frag()
137    offset = len(args)
138    if defaults is not None:
139        offset -= len(defaults)
140    for i in xrange(len(args)):
141        if i == 0:
142            if issubclass(type, meth):
143                if args[i] == "self":
144                    sig.append(arg(self()))
145                elif args[i] == "cls":
146                    sig.append(arg(cls()))
147                else:
148                    sig.append(arg(args[i]))
149            else:
150                sig.append(arg(args[i]))
151        else:
152            if sig:
153                sig.append(u", ")
154            sig.append(arg(args[i]))
155        if i >= offset:
156            sig.append(u"=", lit(repr(defaults[i-offset])))
157    if varargs:
158        if sig:
159            sig.append(u", ")
160        sig.append(u"*", arg(varargs))
161    if varkw:
162        if sig:
163            sig.append(u", ")
164        sig.append(u"**", arg(varkw))
165    sig.insert(0, type(name), u"\u200b(") # use "ZERO WIDTH SPACE" to allow linebreaks
166    sig.append(u")")
167    return sig
168
169
170class _stack(object):
171    def __init__(self, context, obj):
172        self.context = context
173        self.obj = obj
174
175    def __enter__(self):
176        self.context.stack.append(self.obj)
177
178    def __exit__(self, type, value, traceback):
179        self.context.stack.pop()
180
181
182def explain(thing, name=None, format=None, context=[]):
183    """
184    <p>Return a &xml; representation of the documentation of
185    <arg>thing</arg>, which can be a function, method, class or module.</p>
186
187    <p>If <arg>thing</arg> is not a module, you must pass the context
188    in <arg>context</arg>, i.e. a list of names of objects into which <arg>thing</arg>
189    is nested. This means the first entry will always be the module name, and
190    the other entries will be class names.</p>
191    """
192
193    def _append(all, obj, var):
194        try:
195            all.append((_namekey(obj, varname), obj, varname))
196        except (IOError, TypeError):
197            pass
198
199    # Determine visibility
200    visibility = u"public"
201    testname = name or thing.__name__
202    if testname.startswith("_"):
203        visibility = u"protected"
204        if testname.startswith("__"):
205            visibility = u"private"
206            if testname.endswith("__"):
207                visibility = u"special"
208
209    # Determine whether thing has a docstring
210    if format is None and inspect.ismodule(thing):
211        format = getattr(thing, "__docformat__", "plaintext").split()[0]
212    doc = getdoc(thing, format)
213    if doc is xsc.Null:
214        hasdoc = u"nodoc"
215    else:
216        hasdoc = u"doc"
217
218    # Determine type
219    if inspect.ismethod(thing):
220        name = name or thing.__name__
221        context = context + [(thing, name)]
222        (args, varargs, varkw, defaults) = inspect.getargspec(thing.im_func)
223        id = "-".join(info[1] for info in context[1:]) or None
224        sig = xsc.Frag()
225        if name != thing.__name__ and not (thing.__name__.startswith("__") and name=="_" + thing.im_class.__name__ + thing.__name__):
226            sig.append(meth(name), u" = ")
227        sig.append(u"def ", _codeheader(thing.im_func, thing.__name__, meth), u":")
228        return section(h(sig), doc, role=(visibility, u" method ", hasdoc), id=id or None)
229    elif inspect.isfunction(thing):
230        name = name or thing.__name__
231        context = context + [(thing, name)]
232        id = u"-".join(info[1] for info in context[1:]) or None
233        sig = xsc.Frag(
234            u"def ",
235            _codeheader(thing, name, func),
236            u":"
237        )
238        return section(h(sig), doc, role=(visibility, u" function ", hasdoc), id=id)
239    elif isinstance(thing, property):
240        context = context + [(thing, name)]
241        id = u"-".join(info[1] for info in context[1:]) or None
242        sig = xsc.Frag(
243            u"property ", name, u":"
244        )
245        node = section(h(sig), doc, role=(visibility, u" property ", hasdoc), id=id)
246        if thing.fget is not None:
247            node.append(explain(thing.fget, u"__get__", format, context))
248        if thing.fset is not None:
249            node.append(explain(thing.fset, u"__set__", format, context))
250        if thing.fdel is not None:
251            node.append(explain(thing.fdel, u"__delete__", format, context))
252        return node
253    elif inspect.isclass(thing):
254        name = name or thing.__name__
255        context = context + [(thing, name)]
256        id = "-".join(info[1] for info in context[1:]) or None
257        bases = xsc.Frag()
258        if len(thing.__bases__):
259            for baseclass in thing.__bases__:
260                if baseclass.__module__ == "__builtin__":
261                    ref = class_(baseclass.__name__)
262                else:
263                    try:
264                        baseclassname = baseclass.__fullname__
265                    except AttributeError:
266                        baseclassname = baseclass.__name__
267                    if thing.__module__ != baseclass.__module__:
268                        baseclassname4text = baseclass.__module__ + u"." + baseclassname
269                    else:
270                        baseclassname4text = baseclassname
271                    #baseclassname4text = u".\u200b".join(baseclassname4text.split("."))
272                    ref = pyref(class_(baseclassname4text), module=baseclass.__module__, class_=baseclassname)
273                bases.append(ref)
274            bases = bases.withsep(u", ")
275            bases.insert(0, u"\u200b(") # use "ZERO WIDTH SPACE" to allow linebreaks
276            bases.append(u")")
277        node = section(
278            h(
279                u"class ",
280                class_(name),
281                bases,
282                u":"
283            ),
284            doc,
285            role=(visibility, u" class ", hasdoc),
286            id=id
287        )
288
289        # find methods, properties and classes, but filter out those methods that are attribute getters, setters or deleters
290        all = []
291        properties = []
292        classes = []
293        for varname in thing.__dict__.keys():
294            obj = getattr(thing, varname)
295            if isinstance(obj, property):
296                properties.append((obj, varname))
297                _append(all, obj, varname)
298            elif inspect.isclass(obj):
299                for (superclass, supername) in context:
300                    if obj is superclass: # avoid endless recursion for classes that reference a class further up in the context path.
301                        break
302                else:
303                    classes.append((obj, varname))
304                    _append(all, obj, varname)
305            elif inspect.ismethod(obj):
306                # skip the method if it's a property getter, setter or deleter
307                for (prop, name) in properties:
308                    if obj.im_func==prop.fget or obj.im_func==prop.fset or obj.im_func==prop.fdel:
309                        break
310                else:
311                    _append(all, obj, varname)
312        if all:
313            all.sort()
314            for (key, subobj, subname) in all:
315                node.append(explain(subobj, subname, format, context))
316        return node
317    elif inspect.ismodule(thing):
318        name = name or thing.__name__
319        context = [(thing, name)]
320        node = xsc.Frag(doc)
321
322        all = []
323
324        for varname in thing.__dict__:
325            obj = getattr(thing, varname)
326            if inspect.isfunction(obj) or inspect.isclass(obj):
327                _append(all, obj, varname)
328        if all:
329            all.sort()
330            for (key, obj, name) in all:
331                node.append(
332                    explain(obj, name, format, context),
333                )
334        return node
335
336    return xsc.Null
337
338
339class base(xsc.Element):
340    """
341    The base of all element classes. Used for dispatching
342    to conversion targets.
343    """
344    xmlns = xmlns
345    register = False
346
347    class Context(xsc.Element.Context):
348        def __init__(self):
349            xsc.Element.Context.__init__(self)
350            self.stack = []
351            self.sections = [0]
352            self.firstheaderlevel = None
353
354            self.llblue = u"#006499"
355            self.llgreen = u"#9fc94d"
356
357            self.ttfont = u"CourierNew, monospace"
358            self.hdfont = u"ArialNarrow, Arial, sans-serif"
359            self.font = u"PalatinoLinotype, serif"
360
361            self.indentcount = 0
362
363            self.vspaceattrs = dict(
364                space_before=u"0pt",
365                space_after_minimum=u"4pt",
366                space_after_optimum=u"6pt",
367                space_after_maximum=u"12pt",
368                space_after_conditionality=u"discard",
369            )
370
371            self.linkattrs = dict(
372                color=u"blue",
373                text_decoration=u"underline",
374            )
375
376            self.codeattrs = dict(
377                font_family=self.ttfont,
378            )
379
380            self.repattrs = dict(
381                font_style=u"italic",
382            )
383
384            self.emattrs = dict(
385                font_weight=u"bold",
386            )
387
388        def dedent(self):
389            return u"-0.7cm"
390
391        def indent(self):
392            return u"%.1fcm" % (0.7*self.indentcount)
393
394        def labelindent(self):
395            return u"%.1fcm" % (0.7*self.indentcount-0.4)
396
397    def convert(self, converter):
398        target = converter.target
399        if target.xmlns == docbook.xmlns:
400            return self.convert_docbook(converter)
401        elif target.xmlns == html.xmlns:
402            return self.convert_html(converter)
403        elif target.xmlns == xmlns: # our own namespace
404            return self.convert_doc(converter)
405        elif target.xmlns == fo.xmlns:
406            return self.convert_fo(converter)
407        else:
408            raise ValueError("unknown conversion target %r" % target)
409
410    def convert_doc(self, converter):
411        e = self.__class__(
412            self.content.convert(converter),
413            self.attrs.convert(converter)
414        )
415        return e
416
417
418class block(base):
419    """
420    Base class for all block level elements
421    """
422    xmlns = xmlns
423    register = False
424
425    def convert_html(self, converter):
426        e = converter.target.div(self.content)
427        return e.convert(converter)
428
429
430class inline(base):
431    """
432    Base class for all inline elements
433    """
434    xmlns = xmlns
435    register = False
436
437    def convert_html(self, converter):
438        e = converter.target.span(self.content)
439        return e.convert(converter)
440
441
442class abbr(inline):
443    xmlns = xmlns
444    model = sims.NoElements()
445    class Attrs(xsc.Element.Attrs):
446        class title(xsc.TextAttr): pass
447        class lang(xsc.TextAttr): pass
448
449    def convert_docbook(self, converter):
450        e = converter.target.abbrev(self.content, lang=self.attrs.lang)
451        return e.convert(converter)
452
453    def convert_html(self, converter):
454        e = converter.target.abbr(self.content, self.attrs)
455        return e.convert(converter)
456
457    def convert_fo(self, converter):
458        return xsc.Text(unicode(self.content))
459
460    def __unicode__(self):
461        return unicode(self.content)
462
463
464class tab(xsc.Element):
465    """
466    Used for displaying a tab character in the &html; output.
467    """
468    xmlns = xmlns
469    register = False
470
471    def convert(self, converter):
472        e = converter.target.span(u"\xB7\xA0\xA0", class_=u"tab")
473        return e.convert(converter)
474
475
476class litblock(block):
477    """
478    A literal text block (like source code or a shell dump)
479    """
480    xmlns = xmlns
481    model = sims.ElementsOrText(inline)
482
483    cssclass = "litblock"
484
485    def convert_html(self, converter):
486        target = converter.target
487        e = target.pre(class_=self.cssclass)
488        for child in self.content:
489            child = child.convert(converter)
490            if isinstance(child, xsc.Text):
491                for c in child.content:
492                    if c==u"\t":
493                        c = tab()
494                    e.append(c)
495            else:
496                e.append(child)
497        return e.convert(converter)
498
499    def convert_fo(self, converter):
500        target = converter.target
501        context = converter[self]
502        context.indentcount += 1
503        e = target.block(
504            context.vspaceattrs,
505            context.codeattrs,
506            text_align=u"left",
507            line_height=u"130%",
508            font_size=u"90%",
509            start_indent=context.indent(),
510            end_indent=context.indent()
511        )
512        collect = target.block()
513        first = True
514        for child in self.content:
515            child = child.convert(converter)
516            if isinstance(child, xsc.Text):
517                for c in child.content:
518                    # We have to do the following, because FOP doesn't support the white-space property yet
519                    if c==u" ":
520                        c = u"\xa0" # transform spaces into nbsps
521                    if c==u"\t":
522                        c = target.inline(u"\u25ab\xa0\xa0\xa0", color="rgb(50%, 50%, 50%)")
523                    if c==u"\n":
524                        if not collect and not first: # fix empty lines (but not the first one)
525                            collect.append(u"\ufeff")
526                            collect[u"line_height"] = u"60%" # reduce the line-height
527                        e.append(collect)
528                        collect = target.block()
529                        first = False
530                    else:
531                        collect.append(c)
532            else:
533                collect.append(child)
534        if collect:
535            e.append(collect)
536        context.indentcount -= 1
537        return e.convert(converter)
538
539
540class prog(litblock):
541    """
542    A literal listing of all or part of a program
543    """
544    xmlns = xmlns
545    cssclass = u"prog"
546
547    def convert_docbook(self, converter):
548        e = converter.target.programlisting(self.content)
549        return e.convert(converter)
550
551
552class tty(litblock):
553    """
554    A dump of a shell session
555    """
556    xmlns = xmlns
557    cssclass = u"tty"
558
559    def convert_docbook(self, converter):
560        e = converter.target.screen(self.content)
561        return e.convert(converter)
562
563
564class prompt(inline):
565    """
566    The prompt in a <pyref class="tty"><class>tty</class></pyref> dump.
567    """
568    xmlns = xmlns
569
570    def convert_docbook(self, converter):
571        e = converter.target.prompt(self.content)
572        return e.convert(converter)
573
574    def convert_html(self, converter):
575        e = converter.target.code(self.content, class_=u"prompt")
576        return e.convert(converter)
577
578    def convert_fo(self, converter):
579        return xsc.Text(unicode(self.content))
580
581
582class input(inline):
583    """
584    Can be used inside a <pyref class="tty"><class>tty</class></pyref> to mark
585    the parts typed by the user.
586    """
587    xmlns = xmlns
588
589    def convert_docbook(self, converter):
590        e = converter.target.prompt(self.content)
591        return e.convert(converter)
592
593    def convert_html(self, converter):
594        e = converter.target.code(self.content, class_=u"input")
595        return e.convert(converter)
596
597    def convert_fo(self, converter):
598        return xsc.Text(unicode(self.content))
599
600
601class rep(inline):
602    """
603    Content that may or must be replaced by the user
604    """
605    xmlns = xmlns
606    model = sims.NoElements()
607
608    def convert_docbook(self, converter):
609        e = converter.target.replaceable(self.content)
610        return e.convert(converter)
611
612    def convert_html(self, converter):
613        e = converter.target.var(self.content, class_=u"rep")
614        return e.convert(converter)
615
616    def convert_fo(self, converter):
617        e = converter.target.inline(self.content, converter[self].repattrs)
618        return e.convert(converter)
619
620
621class code(inline):
622    xmlns = xmlns
623    register = False
624
625    def convert_fo(self, converter):
626        e = converter.target.inline(
627            self.content,
628            converter[self].codeattrs
629        )
630        return e.convert(converter)
631
632    def convert_html(self, converter):
633        e = converter.target.code(self.content, class_=self.xmlname)
634        return e.convert(converter)
635
636
637class option(code):
638    """
639    An option for a software command
640    """
641    xmlns = xmlns
642    model = sims.ElementsOrText(rep)
643
644    def convert_docbook(self, converter):
645        e = converter.target.option(self.content)
646        return e.convert(converter)
647
648    def convert_html(self, converter):
649        e = converter.target.code(self.content, class_=u"option")
650        return e.convert(converter)
651
652
653class lit(code):
654    """
655    Inline text that is some literal value
656    """
657    xmlns = xmlns
658    model = sims.ElementsOrText(code, rep)
659
660    def convert_docbook(self, converter):
661        e = converter.target.literal(self.content)
662        return e.convert(converter)
663
664    def convert_html(self, converter):
665        e = converter.target.code(self.content, class_=u"lit")
666        return e.convert(converter)
667
668
669class func(code):
670    """
671    The name of a function or subroutine, as in a programming language
672    """
673    xmlns = xmlns
674    model = sims.ElementsOrText(rep)
675
676    def convert_docbook(self, converter):
677        e = converter.target.function(self.content)
678        return e.convert(converter)
679
680
681class meth(code):
682    """
683    The name of a method or memberfunction in a programming language
684    """
685    xmlns = xmlns
686    model = sims.ElementsOrText(rep)
687
688    def convert_docbook(self, converter):
689        e = converter.target.methodname(self.content)
690        return e.convert(converter)
691
692
693class attr(code):
694    """
695    The name of an attribute of a class/object
696    """
697    xmlns = xmlns
698    model = sims.ElementsOrText(rep)
699
700    def convert_docbook(self, converter):
701        e = converter.target.methodname(self.content)
702        return e.convert(converter)
703
704
705class prop(code):
706    """
707    The name of a property in a programming language
708    """
709    xmlns = xmlns
710    model = sims.ElementsOrText(rep)
711
712    def convert_docbook(self, converter):
713        e = converter.target.varname(self.content, role=u"property")
714        return e.convert(converter)
715
716
717class class_(code):
718    """
719    The name of a class, in the object-oriented programming sense
720    """
721    xmlns = xmlns
722    xmlname = "class"
723    model = sims.ElementsOrText(rep)
724
725    def convert_docbook(self, converter):
726        e = converter.target.classname(self.content)
727        return e.convert(converter)
728
729
730class markup(code):
731    """
732    A string of formatting markup in text that is to be represented literally
733    """
734    xmlns = xmlns
735    model = sims.ElementsOrText(rep)
736
737    def convert_docbook(self, converter):
738        e = converter.target.markup(self.content)
739        return e.convert(converter)
740
741    def convert_html(self, converter):
742        e = converter.target.code(self.content, class_=u"markup")
743        return e.convert(converter)
744
745
746class self(code):
747    """
748    <p>use this class when referring to the object for which a method has been
749    called, e.g.:</p>
750    <example>
751    <prog>
752        this function fooifies the object &lt;self/&gt;.
753    </prog>
754    </example>
755    """
756    xmlns = xmlns
757    model = sims.Empty()
758
759    def convert_docbook(self, converter):
760        e = converter.target.varname(u"self")
761        return e.convert(converter)
762
763    def convert_html(self, converter):
764        e = converter.target.code(u"self", class_=u"self")
765        return e.convert(converter)
766
767    def convert_fo(self, converter):
768        e = converter.target.inline(u"self", converter[self].codeattrs)
769        return e.convert(converter)
770
771self_ = self
772
773
774class cls(inline):
775    """
776    <p>use this class when referring to the object for which a class method has been
777    called, e.g.:</p>
778    <example>
779    <prog>
780        this function fooifies the class &lt;cls/&gt;.
781    </prog>
782    </example>
783    """
784    xmlns = xmlns
785    model = sims.Empty()
786
787    def convert_docbook(self, converter):
788        e = converter.target.varname(u"cls")
789        return e.convert(converter)
790
791    def convert_html(self, converter):
792        e = converter.target.code(u"cls", class_=u"cls")
793        return e.convert(converter)
794
795    def convert_fo(self, converter):
796        e = converter.target.inline(u"cls", converter[self].codeattrs)
797        return e.convert(converter)
798
799
800class var(code):
801    """
802    The name of a function or method argument.
803    """
804    xmlns = xmlns
805    model = sims.ElementsOrText(rep, self, cls)
806
807    def convert_docbook(self, converter):
808        e = converter.target.parameter(self.content)
809        return e.convert(converter)
810
811
812class mod(code):
813    """
814    The name of a Python module.
815    """
816    xmlns = xmlns
817    model = sims.ElementsOrText(rep)
818
819    def convert_docbook(self, converter):
820        e = converter.target.classname(self.content, role=u"module")
821        return e.convert(converter)
822
823    def convert_html(self, converter):
824        e = converter.target.code(self.content, class_=u"module")
825        return e.convert(converter)
826
827
828class file(code):
829    """
830    The name of a file
831    """
832    xmlns = xmlns
833    model = sims.ElementsOrText(rep)
834
835    def convert_docbook(self, converter):
836        e = converter.target.filename(self.content)
837        return e.convert(converter)
838
839    def convert_html(self, converter):
840        e = converter.target.code(self.content, class_=u"filename")
841        return e.convert(converter)
842
843
844class dir(code):
845    """
846    The name of directory
847    """
848    xmlns = xmlns
849    model = sims.ElementsOrText(rep)
850
851    def convert_docbook(self, converter):
852        e = converter.target.filename(self.content, class_=u"directory")
853        return e.convert(converter)
854
855
856class user(code):
857    """
858    The name of a user account
859    """
860    xmlns = xmlns
861    model = sims.ElementsOrText(rep)
862
863    def convert_docbook(self, converter):
864        e = converter.target.literal(self.content, role=u"username")
865        return e.convert(converter)
866
867
868class host(code):
869    """
870    The name of a computer
871    """
872    xmlns = xmlns
873    model = sims.ElementsOrText(rep)
874
875    def convert_docbook(self, converter):
876        e = converter.target.literal(self.content, role=u"hostname")
877        return e.convert(converter)
878
879
880class const(code):
881    """
882    The name of a constant
883    """
884    xmlns = xmlns
885    model = sims.ElementsOrText(rep)
886
887    def convert_docbook(self, converter):
888        e = converter.target.literal(self.content, role=u"constant")
889        return e.convert(converter)
890
891
892class data(code):
893    """
894    The name of a data object
895    """
896    xmlns = xmlns
897    model = sims.ElementsOrText(rep)
898
899    def convert_docbook(self, converter):
900        e = converter.target.literal(self.content, role=u"data")
901        return e.convert(converter)
902
903
904class app(inline):
905    """
906    The name of a software program
907    """
908    xmlns = xmlns
909    model = sims.ElementsOrText(rep)
910    class Attrs(xsc.Element.Attrs):
911        class moreinfo(xsc.URLAttr): pass
912
913    def convert_docbook(self, converter):
914        e = converter.target.application(self.content, moreinfo=self.attrs.moreinfo)
915        return e.convert(converter)
916
917    def convert_html(self, converter):
918        if u"moreinfo" in self.attrs:
919            e = converter.target.a(self.content, class_=u"app", href=self.attrs.moreinfo)
920        else:
921            e = converter.target.span(self.content, class_=u"app")
922        return e.convert(converter)
923
924    def convert_fo(self, converter):
925        if u"moreinfo" in self.attrs:
926            e = converter.target.basic_link(
927                self.content,
928                converter[self].linkattrs,
929                external_destination=self.attrs.moreinfo
930            )
931        else:
932            e = self.content
933        return e.convert(converter)
934
935
936class h(base):
937    """
938    The text of the title of a <pyref class="section"><class>section</class></pyref>
939    or an <pyref class="example"><class>example</class></pyref>
940    """
941    xmlns = xmlns
942    model = sims.ElementsOrText(inline)
943
944    def convert_docbook(self, converter):
945        e = converter.target.title(self.content.convert(converter))
946        return e.convert(converter)
947
948    def convert_html(self, converter):
949        context = converter[self]
950        if context.stack:
951            if isinstance(context.stack[-1], example):
952                e = self.content
953            elif isinstance(context.stack[-1], section):
954                level = len(context.sections)
955                if context.firstheaderlevel is None:
956                    context.firstheaderlevel = level
957                e = getattr(converter.target, "h%d" % (context.firstheaderlevel+level), converter.target.h6)(self.content)
958            else:
959                raise ValueError("unknown node %r on the stack" % context.stack[-1])
960        else:
961            context.firstheaderlevel = 0
962            e = converter.target.h1(self.content)
963        return e.convert(converter)
964
965    def convert_fo(self, converter):
966        e = self.content
967        return e.convert(converter)
968
969
970class section(block):
971    """
972    A recursive section
973    """
974    xmlns = xmlns
975    model = sims.Elements(h, block)
976    class Attrs(xsc.Element.Attrs):
977        class role(xsc.TextAttr): pass
978        class id(xsc.IDAttr): pass
979
980    def convert_docbook(self, converter):
981        e = converter.target.section(self.content, role=self.attrs.role, id=self.attrs.id)
982        return e.convert(converter)
983
984    def convert_html(self, converter):
985        target = converter.target
986        context = converter[self]
987        context.sections[-1] += 1
988        level = len(context.sections)
989        context.sections.append(0) # for numbering the subsections
990        ts = xsc.Frag()
991        cs = html.div(class_=u"content")
992        for child in self:
993            if isinstance(child, h):
994                ts.append(child)
995            else:
996                cs.append(child)
997        e = target.div(class_=(u"section level", level), id=self.attrs.id)
998        if "role" in self.attrs:
999            e.attrs.class_.append(" ", self.attrs.role)
1000        #if "id" in self.attrs:
1001        #   e.append(target.a(name=self.attrs.id, id=self.attrs.id))
1002        hclass = getattr(target, u"h%d" % level, target.h6)
1003        for t in ts:
1004            e.append(hclass(t.content))
1005        e.append(cs)
1006        with _stack(context, self):
1007            # make sure to call the inner convert() before popping the number off of the stack
1008            e = e.convert(converter)
1009            del context.sections[-1]
1010            return e
1011
1012    def convert_fo(self, converter):
1013        context = converter[self]
1014        context.sections[-1] += 1
1015        context.sections.append(0)
1016        ts = xsc.Frag()
1017        cs = xsc.Frag()
1018        props = [
1019            # size,    before,  after
1020            (u"30pt", u"30pt", u"2pt"),
1021            (u"22pt", u"20pt", u"2pt"),
1022            (u"16pt", u"15pt", u"2pt"),
1023            (u"12pt", u"15pt", u"2pt")
1024        ]
1025        for child in self.content:
1026            if isinstance(child, h):
1027                ts.append(child.content)
1028            else:
1029                cs.append(child)
1030        p = props[min(len(context.sections)-1, len(props)-1)]
1031        isref = unicode(self.attrs.role.convert(converter)) in (u"class", u"method", u"property", u"function", u"module")
1032
1033        number = None
1034        if isref:
1035            context.indentcount += 1
1036            text_indent = context.dedent()
1037        else:
1038            if len(context.sections)>1:
1039                number = (
1040                    u".".join(unicode(s) for s in context.sections[:-1]),
1041                    u". "
1042                )
1043            text_indent = None
1044
1045        tattrs = fo.block.Attrs(
1046            font_size=p[0],
1047            color=context.llblue,
1048            space_before=p[1],
1049            space_after=p[2],
1050            text_align=u"left",
1051            font_family=context.hdfont,
1052            keep_with_next_within_page=u"always",
1053            text_indent=text_indent
1054        )
1055        e = fo.block(
1056            fo.block(number, ts, tattrs),
1057            cs,
1058            start_indent=context.indent()
1059        )
1060        e = e.convert(converter)
1061        del context.sections[-1]
1062        if isref:
1063            context.indentcount -= 1
1064        return e
1065
1066
1067class p(block):
1068    """
1069    A paragraph
1070    """
1071    xmlns = xmlns
1072    model = sims.ElementsOrText(inline)
1073    class Attrs(xsc.Element.Attrs):
1074        class type(xsc.TextAttr): pass
1075
1076    def convert_docbook(self, converter):
1077        e = converter.target.para(self.content, role=self.attrs.type)
1078        return e.convert(converter)
1079
1080    def convert_html(self, converter):
1081        e = converter.target.p(self.content, class_=self.attrs.type)
1082        return e.convert(converter)
1083
1084    def convert_fo(self, converter):
1085        e = fo.block(
1086            self.content,
1087            converter[self].vspaceattrs,
1088            line_height=u"130%"
1089        )
1090        return e.convert(converter)
1091
1092
1093class dt(block):
1094    """
1095    A term inside a <pyref class="dl"><class>dl</class></pyref>
1096    """
1097    xmlns = xmlns
1098    model = sims.ElementsOrText(inline)
1099
1100    def convert_docbook(self, converter):
1101        e = converter.target.term(self.content)
1102        return e.convert(converter)
1103
1104    def convert_html(self, converter):
1105        e = converter.target.dt(self.content)
1106        return e.convert(converter)
1107
1108    def convert_fo(self, converter):
1109        e = converter.target.block(
1110            self.content,
1111            font_style=u"italic"
1112        )
1113        return e.convert(converter)
1114
1115
1116class li(block):
1117    """
1118    A wrapper for the elements of a list item in
1119    <pyref class="ul"><class>ul</class></pyref> or
1120    <pyref class="ol"><class>ol</class></pyref>.
1121    """
1122    xmlns = xmlns
1123    model = sims.ElementsOrText(block, inline) # if it contains no block elements, the content will be promoted to a paragraph
1124
1125    def convert_docbook(self, converter):
1126        if self[block]:
1127            content = self.content
1128        else:
1129            content = converter.target.para(self.content)
1130        e = converter.target.listitem(content)
1131        return e.convert(converter)
1132
1133    def convert_html(self, converter):
1134        e = converter.target.li(self.content)
1135        return e.convert(converter)
1136
1137    def convert_fo(self, converter):
1138        target = converter.target
1139        context = converter[self]
1140        context.lists[-1][1] += 1
1141        type = context.lists[-1][0]
1142        if type=="ul":
1143            label = u"\u2022"
1144        elif type=="ol":
1145            label = "%d." % context.lists[-1][1]
1146        context.indentcount += 1
1147        if self[block]: # Do we have a block in our content?
1148            content = self.content # yes => use the content as is
1149        else:
1150            content = p(self.content) # no => wrap it in a paragraph
1151        e = target.list_item(
1152            target.list_item_label(
1153                target.block(label),
1154                start_indent=context.labelindent()
1155            ),
1156            target.list_item_body(
1157                content,
1158                start_indent=context.indent()
1159            )
1160        )
1161        context.indentcount -= 1
1162        return e.convert(converter)
1163
1164
1165class dd(block):
1166    """
1167    A wrapper for the elements of a list item
1168    <pyref class="dl"><class>dl</class></pyref>
1169    """
1170    xmlns = xmlns
1171    model = sims.ElementsOrText(block, inline) # if it contains no block elements, the content will be promoted to a paragraph
1172
1173    def convert_docbook(self, converter):
1174        if self[block]:
1175            content = self.content
1176        else:
1177            content = converter.target.para(self.content)
1178        e = converter.target.listitem(content)
1179        return e.convert(converter)
1180
1181    def convert_html(self, converter):
1182        e = converter.target.dd(self.content)
1183        return e.convert(converter)
1184
1185    def convert_fo(self, converter):
1186        target = converter.target
1187        context = converter[self]
1188        context.lists[-1][1] += 1
1189        type = context.lists[-1][0]
1190        context.indentcount += 1
1191        if self[block]: # Do we have a block in our content?
1192            content = self.content # yes => use the content as is
1193        else:
1194            content = p(self.content) # no => wrap it in a paragraph
1195        e = target.block(
1196            content,
1197            start_indent=context.indent()
1198        )
1199        context.indentcount -= 1
1200        return e.convert(converter)
1201
1202
1203class list(block):
1204    """
1205    Common baseclass for <pyref class="ul"><class>ul</class></pyref>,
1206    <pyref class="ol"><class>ol</class></pyref> and
1207    <pyref class="dl"><class>dl</class></pyref>.
1208    """
1209    xmlns = xmlns
1210    register = False
1211
1212
1213class ul(list):
1214    """
1215    A list in which each entry is marked with a bullet or other dingbat
1216    """
1217    xmlns = xmlns
1218    model = sims.Elements(li)
1219
1220    def convert_docbook(self, converter):
1221        e = converter.target.itemizedlist(self.content.convert(converter))
1222        return e.convert(converter)
1223
1224    def convert_html(self, converter):
1225        with _stack(converter[self], self):
1226            return converter.target.ul(self.content.convert(converter))
1227
1228    def convert_fo(self, converter):
1229        context = converter[self]
1230        context.lists.append(["ul", 0])
1231        e = converter.target.list_block(self.content, line_height=u"130%")
1232        e = e.convert(converter)
1233        del context.lists[-1]
1234        return e
1235
1236
1237class ol(list):
1238    """
1239    A list in which each entry is marked with a sequentially incremented label
1240    """
1241    xmlns = xmlns
1242    model = sims.Elements(li)
1243
1244    def convert_docbook(self, converter):
1245        e = converter.target.orderedlist(self.content.convert(converter))
1246        return e.convert(converter)
1247
1248    def convert_html(self, converter):
1249        with _stack(converter[self], self):
1250            return converter.target.ol(self.content.convert(converter))
1251
1252    def convert_fo(self, converter):
1253        context = converter[self]
1254        context.lists.append(["ol", 0])
1255        e = converter.target.list_block(self.content, line_height=u"130%")
1256        e = e.convert(converter)
1257        del context.lists[-1]
1258        return e
1259
1260
1261class dl(list):
1262    """
1263    A list in which each entry is marked with a label
1264    """
1265    xmlns = xmlns
1266    model = sims.Elements(dt, dd)
1267
1268    def convert_docbook(self, converter):
1269        e = converter.target.variablelist()
1270        collect = converter.target.varlistentry()
1271        for child in self.content:
1272            collect.append(child)
1273            if isinstance(child, dd):
1274                e.append(collect)
1275                collect = converter.target.varlistentry()
1276        if collect:
1277            e.append(collect)
1278        return e.convert(converter)
1279
1280    def convert_html(self, converter):
1281        with _stack(converter[self], self):
1282            return converter.target.dl(self.content.convert(converter))
1283
1284    def convert_fo(self, converter):
1285        context = converter[self]
1286        context.lists.append(["dl", 0])
1287        e = self.content.convert(converter)
1288        del context.lists[-1]
1289        return e
1290
1291
1292class example(block):
1293    """
1294    A formal example
1295    """
1296    xmlns = xmlns
1297    model = sims.Elements(h, block)
1298
1299    def convert_docbook(self, converter):
1300        e = converter.target.example(self.content)
1301        return e.convert(converter)
1302
1303    def convert_html(self, converter):
1304        target = converter.target
1305        ts = xsc.Frag()
1306        e = xsc.Frag()
1307        for child in self:
1308            if isinstance(child, h):
1309                ts.append(child)
1310            else:
1311                e.append(child)
1312        if ts:
1313            e.append(target.div(ts, class_=u"example-title"))
1314        with _stack(converter[self], self):
1315            return e.convert(converter)
1316
1317    def convert_fo(self, converter):
1318        # FIXME: handle title
1319        e = xsc.Frag()
1320        for child in self.content:
1321            if not isinstance(child, h):
1322                e.append(child)
1323        return e.convert(converter)
1324
1325
1326class a(inline):
1327    """
1328    A hypertext link.
1329    """
1330    xmlns = xmlns
1331    model = sims.ElementsOrText(inline)
1332    class Attrs(xsc.Element.Attrs):
1333        class href(xsc.URLAttr): pass
1334        class hreflang(xsc.TextAttr): pass
1335
1336    def convert_docbook(self, converter):
1337        e = converter.target.link(self.content, linkend=self.attrs.href)
1338        return e.convert(converter)
1339
1340    def convert_html(self, converter):
1341        e = converter.target.a(self.content, href=self.attrs.href, hreflang=self.attrs.hreflang)
1342        return e.convert(converter)
1343
1344    def convert_fo(self, converter):
1345        if u"href" in self.attrs:
1346            e = converter.target.basic_link(
1347                self.content,
1348                converter[self].linkattrs,
1349                external_destination=self.attrs.href
1350            )
1351        else:
1352            e = self.content
1353        return e.convert(converter)
1354
1355
1356class xref(inline):
1357    """
1358    An internal cross reference.
1359    """
1360    xmlns = xmlns
1361    model = sims.ElementsOrText(inline)
1362    class Attrs(xsc.Element.Attrs):
1363        class ref(xsc.TextAttr): pass
1364
1365    def convert_docbook(self, converter):
1366        e = converter.target.link(self.content, linkend=self.attrs.ref)
1367        return e.convert(converter)
1368
1369    def convert_html(self, converter):
1370        e = converter.target.a(self.content, href=(u"#", self.attrs.ref))
1371        return e.convert(convertert)
1372
1373    def convert_fo(self, converter):
1374        if u"href" in self.attrs:
1375            e = converter.target.basic_link(
1376                self.content,
1377                converter[self].linkattrs,
1378                internal_destination=self.attrs.ref
1379            )
1380        else:
1381            e = self.content
1382        return e.convert(converter)
1383
1384
1385class email(inline):
1386    """
1387    An email address.
1388    """
1389    xmlns = xmlns
1390    model = sims.NoElements()
1391
1392    def convert_docbook(self, converter):
1393        e = converter.target.email(self.content)
1394        return e.convert(converter)
1395
1396    def convert_html(self, converter):
1397        e = converter.target.a(self.content, href=(u"mailto:", self.content))
1398        return e.convert(converter)
1399
1400    def convert_fo(self, converter):
1401        e = converter.target.basic_link(
1402            self.content,
1403            converter[self].linkattrs,
1404            external_destination=(u"mailto:", self.content)
1405        )
1406        return e.convert(converter)
1407
1408
1409class em(inline):
1410    """
1411    Emphasized text
1412    """
1413    xmlns = xmlns
1414    model = sims.ElementsOrText(inline)
1415
1416    def convert_docbook(self, converter):
1417        e = converter.target.emphasis(self.content)
1418        return e.convert(converter)
1419
1420    def convert_html(self, converter):
1421        e = converter.target.em(self.content)
1422        return e.convert(converter)
1423
1424    def convert_fo(self, converter):
1425        e = converter.target.inline(
1426            self.content,
1427            converter[self].emattrs
1428        )
1429        return e.convert(converter)
1430
1431
1432class pyref(inline):
1433    """
1434    reference to a Python object:
1435    module, class, method, property or function
1436    """
1437    xmlns = xmlns
1438    model = sims.ElementsOrText(inline)
1439    class Attrs(xsc.Element.Attrs):
1440        class module(xsc.TextAttr): pass
1441        class class_(xsc.TextAttr): xmlname = u"class"
1442        class method(xsc.TextAttr): pass
1443        class property(xsc.TextAttr): pass
1444        class function(xsc.TextAttr): pass
1445
1446    class Context(xsc.Element.Context):
1447        def __init__(self):
1448            xsc.Element.Context.__init__(self)
1449            self.base = "http://127.0.0.1:7464/"
1450
1451    def convert(self, converter):
1452        target = converter.target
1453        context = converter[self]
1454        if target.xmlns == xmlns: # our own namespace
1455            return self.convert_doc(converter)
1456        if u"function" in self.attrs:
1457            function = unicode(self.attrs.function.convert(converter))
1458        else:
1459            function = None
1460        if u"method" in self.attrs:
1461            method = unicode(self.attrs.method.convert(converter))
1462        else:
1463            method = None
1464        if u"property" in self.attrs:
1465            prop = unicode(self.attrs.property.convert(converter))
1466        else:
1467            prop = None
1468        if u"class_" in self.attrs:
1469            class__ = unicode(self.attrs.class_.convert(converter)).replace(u".", u"-")
1470        else:
1471            class__ = None
1472        if u"module" in self.attrs:
1473            module = unicode(self.attrs.module.convert(converter))
1474            if module.startswith(u"ll."):
1475                module = module[3:].replace(u".", u"/")
1476            elif module == "ll":
1477                module = "core"
1478            else:
1479                module = None
1480        else:
1481            module = None
1482
1483        e = self.content
1484        if target.xmlns == html.xmlns:
1485            if function is not None:
1486                if module is not None:
1487                    e = target.a(e, href=(context.base, module, u"/index.html#", function))
1488            elif method is not None:
1489                if class__ is not None and module is not None:
1490                    e = target.a(e, href=(context.base, module, u"/index.html#", class__, "-", method))
1491            elif prop is not None:
1492                if class__ is not None and module is not None:
1493                    e = target.a(e, href=(context.base, module, u"/index.html#", class__, "-", prop))
1494            elif class__ is not None:
1495                if module is not None:
1496                    e = target.a(e, href=(context.base, module, u"/index.html#", class__))
1497            elif module is not None:
1498                e = target.a(e, href=(context.base, module, u"/index.html"))
1499        return e.convert(converter)
1500
1501
1502class fodoc(base):
1503    xmlns = xmlns
1504    model = sims.Elements(block)
1505
1506    def convert(self, converter):
1507        context = converter[self]
1508        e = self.content
1509        converter.push(target=sys.modules[__name__]) # our own module
1510        e = e.convert(converter)
1511        converter.pop()
1512        converter.push(target=fo)
1513        e = e.convert(converter)
1514        converter.pop()
1515
1516        e = xsc.Frag(
1517            xml.XML(), u"\n",
1518            fo.root(
1519                fo.layout_master_set(
1520                    fo.simple_page_master(
1521                        fo.region_body(
1522                            region_name=u"xsl-region-body",
1523                            margin_bottom=u"3cm"
1524                        ),
1525                        fo.region_after(
1526                            region_name=u"xsl-region-after",
1527                            extent=u"2cm"
1528                        ),
1529                        master_name=u"default",
1530                        page_height=u"29.7cm",
1531                        page_width=u"21cm",
1532                        margin_top=u"1cm",
1533                        margin_bottom=u"1cm",
1534                        margin_left=u"2.5cm",
1535                        margin_right=u"1cm"
1536                    )
1537                ),
1538                fo.page_sequence(
1539                    fo.static_content(
1540                        fo.block(
1541                            fo.page_number(),
1542                            border_before_width=u"0.1pt",
1543                            border_before_color=u"#000",
1544                            border_before_style=u"solid",
1545                            padding_before=u"4pt",
1546                            text_align=u"center"
1547                        ),
1548                        flow_name=u"xsl-region-after"
1549                    ),
1550                    fo.flow(
1551                        e,
1552                        flow_name=u"xsl-region-body"
1553                    ),
1554                    master_reference=u"default"
1555                ),
1556                font_family=context.font,
1557                font_size=u"10pt",
1558                text_align=u"justify",
1559                line_height=u"normal",
1560                language=u"en",
1561                orphans=2,
1562                widows=3
1563            )
1564        )
1565        return e
Note: See TracBrowser for help on using the browser.