Source code for ll.xist.ns.doc

# -*- coding: utf-8 -*-
# cython: language_level=3, always_allow_keywords=True

## Copyright 1999-2024 by LivingLogic AG, Bayreuth/Germany
## Copyright 1999-2024 by Walter Dörwald
##
## All Rights Reserved
##
## See ll/xist/__init__.py for the license


"""
This namespace module provides classes that can be used for generating
documentation (in HTML, DocBook and XSL-FO).
"""


import sys, types, inspect, textwrap, warnings, operator

import ll
from ll.xist import xsc, parse, sims, xfind
from ll.xist.ns import html, docbook, fo, specials, xml


__docformat__ = "reStructuredText"


xmlns = "http://xmlns.livinglogic.de/xist/ns/doc"


def _getmodulename(thing):
	module = inspect.getmodule(thing)
	if module is None:
		return "???"
	else:
		return module.__name__


[docs] def getdoc(thing, format): """ Return the docstring for ``thing``, as an XIST node using this namespace module. ``format`` specifies how to treat the docstring: ``"plaintext"`` Treat to docstring as text. This returns a single :class:`~ll.xist.xsc.Text` node. ``"restructuredtext"`` This interprets the docstring as ReST source and converts it to use this namespace. ``"xist"`` This treats the docstring as XML, which will be parsed using this namespace as the default namespace. """ if thing.__doc__ is None: return xsc.Null # Remove indentation lines = textwrap.dedent(thing.__doc__).split("\n") # remove empty lines while lines and not lines[0]: del lines[0] while lines and not lines[-1]: del lines[-1] text = "\n".join(lines) modulename = _getmodulename(thing) if inspect.ismethod(thing): base = f"METHOD-DOCSTRING({modulename}.{thing.__class__.__name__}.{thing.__qualname__})" elif isinstance(thing, property): base = f"PROPERTY-DOCSTRING({modulename}.{thing})" elif inspect.isfunction(thing): base = f"FUNCTION-DOCSTRING({modulename}.{thing.__qualname__})" elif inspect.isclass(thing): base = f"CLASS-DOCSTRING({modulename}.{thing.__qualname__})" elif inspect.ismodule(thing): base = f"MODULE-DOCSTRING({modulename})" else: base = "DOCSTRING" lformat = format.lower() if lformat == "plaintext": return xsc.Text(text) elif lformat == "restructuredtext": from ll.xist.ns import rest, doc return rest.fromstring(text, base=base).conv(target=doc) elif lformat == "xist": from ll.xist.ns import doc node = parse.tree(parse.String(text), parse.SGMLOP(), parse.NS(doc), parse.Node(pool=xsc.docpool(), base=base)) if not node[p]: # optimization: one paragraph docstrings don't need a <p> element. node = p(node) if inspect.ismethod(thing): # Use the original method instead of the decorator realthing = thing while hasattr(realthing, "__wrapped__"): realthing = realthing.__wrapped__ for ref in node.walknodes(pyref): if "module" not in ref.attrs: ref["module"] = _getmodulename(realthing) if "class_" not in ref.attrs: ref["class_"] = thing.__self__.__class__.__name__ if "method" not in ref.attrs: ref["method"] = thing.__name__ elif inspect.isfunction(thing): # Use the original method instead of the decorator while hasattr(thing, "__wrapped__"): thing = thing.__wrapped__ for ref in node.walknodes(pyref): if "module" not in ref.attrs: ref["module"] = _getmodulename(thing) elif inspect.isclass(thing): for ref in node.walknodes(pyref): if "module" not in ref.attrs: ref["module"] = _getmodulename(thing) if "class_" not in ref.attrs: ref["class_"] = thing.__name__ elif inspect.ismodule(thing): for ref in node.walknodes(pyref): if "module" not in ref.attrs: ref["module"] = thing.__name__ return node else: raise ValueError(f"unsupported __docformat__ {format!r}")
def getsourceline(obj): if isinstance(obj, property): pos = 999999999 if obj.fget is not None: pos = min(pos, obj.fget.__code__.co_firstlineno) if obj.fset is not None: pos = min(pos, obj.fset.__code__.co_firstlineno) if obj.fdel is not None: pos = min(pos, obj.fdel.__code__.co_firstlineno) else: while hasattr(obj, "__wrapped__"): obj = obj.__wrapped__ try: # FIXME: Python SF bug #1224621 pos = inspect.getsourcelines(obj)[-1] except IndentationError: pos = 42 return pos def _namekey(obj, name): return (getsourceline(obj), name or obj.__name__) def _codeheader(thing, name, type): spec = inspect.getfullargspec(thing) sig = xsc.Frag() offset = len(spec.args) if spec.defaults is not None: offset -= len(spec.defaults) for (i, arg) in enumerate(spec.args): if i == 0: if issubclass(type, meth): if arg == "self": sig.append(obj(self())) elif arg == "cls": sig.append(obj(cls())) else: sig.append(obj(arg)) else: sig.append(obj(arg)) else: if sig: sig.append(", ") sig.append(obj(arg)) if i >= offset: sig.append("=", lit(repr(spec.defaults[i-offset]))) if spec.varargs: if sig: sig.append(", ") sig.append("*", obj(spec.varargs)) if spec.varkw: if sig: sig.append(", ") sig.append("**", obj(spec.varkw)) for (i, arg) in enumerate(spec.kwonlyargs): if sig: sig.append(", ") sig.append(obj(arg)) if arg in spec.kwonlydefaults: sig.append("=", lit(repr(spec.kwonlydefaults[arg]))) sig.insert(0, type(name), "\u200b(") # use "ZERO WIDTH SPACE" to allow linebreaks sig.append(")") return sig class _stack: def __init__(self, context, obj): self.context = context self.obj = obj def __enter__(self): self.context.stack.append(self.obj) def __exit__(self, type, value, traceback): self.context.stack.pop()
[docs] def explain(thing, name=None, format=None, context=[]): """ Return a XML representation of the doc string of ``thing``, which can be a function, method, class or module. If ``thing`` is not a module, you must pass the context in ``context``, i.e. a list of names of objects into which ``thing`` is nested. This means the first entry will always be the module name, and the other entries will be class names. For the meaning for ``format`` see, :func:`getdoc`. """ def _append(all, obj, varname): try: all.append((_namekey(obj, varname), obj, varname)) except (IOError, TypeError): pass # Determine visibility visibility = "public" testname = name or thing.__name__ if testname.startswith("_"): visibility = "protected" if testname.startswith("__"): visibility = "private" if testname.endswith("__"): visibility = "special" # Determine whether ``thing`` has a docstring if format is None and inspect.ismodule(thing): format = getattr(thing, "__docformat__", "plaintext").split()[0] doc = getdoc(thing, format) if doc is xsc.Null: hasdoc = "nodoc" else: hasdoc = "doc" # Determine type if inspect.ismethod(thing): name = name or thing.__name__ context = context + [(thing, name)] id = "-".join(info[1] for info in context[1:]) or None sig = xsc.Frag() if name != thing.__name__ and not (thing.__name__.startswith("__") and name=="_" + thing.__self__.__class__.__name__ + thing.__name__): sig.append(meth(name), " = ") sig.append("def ", _codeheader(thing.__func__, thing.__name__, meth), ":") return section(h(sig), doc, role=(visibility, " method ", hasdoc), id=id) elif inspect.isfunction(thing): name = name or thing.__name__ context = context + [(thing, name)] id = "-".join(info[1] for info in context[1:]) or None sig = xsc.Frag( "def ", _codeheader(thing, name, func), ":" ) return section(h(sig), doc, role=(visibility, " function ", hasdoc), id=id) elif isinstance(thing, property): context = context + [(thing, name)] id = "-".join(info[1] for info in context[1:]) or None sig = xsc.Frag( "property ", name, ":" ) node = section(h(sig), doc, role=(visibility, " property ", hasdoc), id=id) if thing.fget is not None: node.append(explain(thing.fget, "__get__", format, context)) if thing.fset is not None: node.append(explain(thing.fset, "__set__", format, context)) if thing.fdel is not None: node.append(explain(thing.fdel, "__delete__", format, context)) return node elif inspect.isclass(thing): name = name or thing.__name__ context = context + [(thing, name)] id = "-".join(info[1] for info in context[1:]) or None bases = xsc.Frag() if len(thing.__bases__): for baseclass in thing.__bases__: if baseclass.__module__ in ("builtins", "exceptions"): ref = class_(baseclass.__name__) else: try: baseclassname = baseclass.__qualname__ except AttributeError: baseclassname = baseclass.__name__ if thing.__module__ != baseclass.__module__: baseclassname4text = baseclass.__module__ + "." + baseclassname else: baseclassname4text = baseclassname #baseclassname4text = u".\u200b".join(baseclassname4text.split(".")) ref = pyref(class_(baseclassname4text), module=baseclass.__module__, class_=baseclassname) bases.append(ref) bases = bases.withsep(", ") bases.insert(0, "\u200b(") # use "ZERO WIDTH SPACE" to allow linebreaks bases.append(")") node = section( h( "class ", class_(name), bases, ":" ), doc, role=(visibility, " class ", hasdoc), id=id ) # find methods, properties and classes, but filter out those methods that are attribute getters, setters or deleters all = [] properties = [] classes = [] for varname in thing.__dict__: obj = getattr(thing, varname) if isinstance(obj, property): properties.append((obj, varname)) _append(all, obj, varname) elif inspect.isclass(obj): for (superclass, supername) in context: if obj is superclass: # avoid endless recursion for classes that reference a class further up in the context path. break else: classes.append((obj, varname)) _append(all, obj, varname) elif inspect.isfunction(obj): # skip the method if it's a property getter, setter or deleter for (prop, name) in properties: if obj==prop.fget or obj==prop.fset or obj==prop.fdel: break else: _append(all, obj, varname) if all: all.sort() for (key, subobj, subname) in all: node.append(explain(subobj, subname, format, context)) return node elif inspect.ismodule(thing): name = name or thing.__name__ context = [(thing, name)] node = xsc.Frag(doc) all = [] for varname in thing.__dict__: obj = getattr(thing, varname) if inspect.isfunction(obj) or inspect.isclass(obj): _append(all, obj, varname) if all: all.sort() for (key, obj, name) in all: node.append( explain(obj, name, format, context), ) return node return xsc.Null
[docs] class base(xsc.Element): """ The base of all element classes. Used for dispatching to conversion targets. """ xmlns = xmlns register = False class Context(xsc.Element.Context): def __init__(self): xsc.Element.Context.__init__(self) self.stack = [] self.sections = [0] self.firstheaderlevel = None self.llblue = "#006499" self.llgreen = "#9fc94d" self.ttfont = "CourierNew, monospace" self.hdfont = "ArialNarrow, Arial, sans-serif" self.font = "PalatinoLinotype, serif" self.indentcount = 0 self.vspaceattrs = dict( space_before="0pt", space_after_minimum="4pt", space_after_optimum="6pt", space_after_maximum="12pt", space_after_conditionality="discard", ) self.linkattrs = dict( color="blue", text_decoration="underline", ) self.codeattrs = dict( font_family=self.ttfont, ) self.repattrs = dict( font_style="italic", ) self.emattrs = dict( font_style="italic", ) self.strongattrs = dict( font_weight="bold", ) def dedent(self): return "-0.7cm" def indent(self): return f"{0.7*self.indentcount:.1f}cm" def labelindent(self): return f"{0.7*self.indentcount-0.4:.1f}cm" def convert(self, converter): target = converter.target if target.xmlns == docbook.xmlns: return self.convert_docbook(converter) elif target.xmlns == html.xmlns: return self.convert_html(converter) elif target.xmlns == xmlns: # our own namespace return self.convert_doc(converter) elif target.xmlns == fo.xmlns: return self.convert_fo(converter) else: raise ValueError(f"unknown conversion target {target!r}") def convert_doc(self, converter): e = self.__class__( self.content.convert(converter), self.attrs.convert(converter) ) return e
[docs] class block(base): """ Base class for all block level elements. """ xmlns = xmlns register = False def convert_html(self, converter): e = converter.target.div(self.content) return e.convert(converter)
[docs] class inline(base): """ Base class for all inline elements. """ xmlns = xmlns register = False def convert_html(self, converter): e = converter.target.span(self.content) return e.convert(converter)
[docs] class abbr(inline): """ Abbreviation. """ xmlns = xmlns model = sims.NoElements() class Attrs(xsc.Element.Attrs): class title(xsc.TextAttr): pass class lang(xsc.TextAttr): pass def convert_docbook(self, converter): e = converter.target.abbrev(self.content, lang=self.attrs.lang) return e.convert(converter) def convert_html(self, converter): e = converter.target.abbr(self.content, self.attrs) return e.convert(converter) def convert_fo(self, converter): return xsc.Text(str(self.content)) def __str__(self): return str(self.content)
[docs] class tab(xsc.Element): """ Used for displaying a tab character in the HTML output. """ xmlns = xmlns register = False def convert(self, converter): e = converter.target.span("\xB7\xA0\xA0", class_="tab") return e.convert(converter)
[docs] class litblock(block): """ A literal text block (like source code or a shell session). """ xmlns = xmlns model = sims.ElementsOrText(inline) cssclass = "litblock" def convert_html(self, converter): target = converter.target e = target.pre(class_=self.cssclass) for child in self.content: child = child.convert(converter) if isinstance(child, xsc.Text): for c in child.content: if c=="\t": c = tab() e.append(c) else: e.append(child) return e.convert(converter) def convert_fo(self, converter): target = converter.target context = converter[self] context.indentcount += 1 e = target.block( context.vspaceattrs, context.codeattrs, text_align="left", line_height="130%", font_size="90%", start_indent=context.indent(), end_indent=context.indent() ) collect = target.block() first = True for child in self.content: child = child.convert(converter) if isinstance(child, xsc.Text): for c in child.content: # We have to do the following, because FOP doesn't support the white-space property yet if c==" ": c = "\xa0" # transform spaces into nbsps if c=="\t": c = target.inline("\u25ab\xa0\xa0\xa0", color="rgb(50%, 50%, 50%)") if c=="\n": if not collect and not first: # fix empty lines (but not the first one) collect.append("\ufeff") collect["line_height"] = "60%" # reduce the line-height e.append(collect) collect = target.block() first = False else: collect.append(c) else: collect.append(child) if collect: e.append(collect) context.indentcount -= 1 return e.convert(converter)
[docs] class prog(litblock): """ A literal listing of all or part of a program. """ xmlns = xmlns cssclass = "prog" def convert_docbook(self, converter): e = converter.target.programlisting(self.content) return e.convert(converter)
[docs] class tty(litblock): """ A dump of a shell session. """ xmlns = xmlns cssclass = "tty" def convert_docbook(self, converter): e = converter.target.screen(self.content) return e.convert(converter)
[docs] class prompt(inline): """ The prompt in a :class:`tty` dump. """ xmlns = xmlns def convert_docbook(self, converter): e = converter.target.prompt(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="prompt") return e.convert(converter) def convert_fo(self, converter): return xsc.Text(str(self.content))
[docs] class input(inline): """ Can be used inside a :class:`tty` to mark the parts typed by the user. """ xmlns = xmlns def convert_docbook(self, converter): e = converter.target.prompt(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="input") return e.convert(converter) def convert_fo(self, converter): return xsc.Text(str(self.content))
[docs] class rep(inline): """ Content that may or must be replaced by the user. """ xmlns = xmlns model = sims.NoElements() def convert_docbook(self, converter): e = converter.target.replaceable(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.var(self.content, class_="rep") return e.convert(converter) def convert_fo(self, converter): e = converter.target.inline(self.content, converter[self].repattrs) return e.convert(converter)
class code(inline): xmlns = xmlns register = False def convert_fo(self, converter): e = converter.target.inline( self.content, converter[self].codeattrs ) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_=self.xmlname) return e.convert(converter)
[docs] class option(code): """ An option for a software command. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.option(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="option") return e.convert(converter)
[docs] class lit(code): """ Inline text that is some literal value. """ xmlns = xmlns model = sims.ElementsOrText(code, rep) def convert_docbook(self, converter): e = converter.target.literal(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="lit") return e.convert(converter)
[docs] class func(code): """ The name of a function or subroutine, as in a programming language. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.function(self.content) return e.convert(converter)
[docs] class meth(code): """ The name of a method or memberfunction in a programming language. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.methodname(self.content) return e.convert(converter)
[docs] class attr(code): """ The name of an attribute of a class/object. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.methodname(self.content) return e.convert(converter)
[docs] class prop(code): """ The name of a property in a programming language. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.varname(self.content, role="property") return e.convert(converter)
[docs] class class_(code): """ The name of a class, in the object-oriented programming sense. """ xmlns = xmlns xmlname = "class" model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.classname(self.content) return e.convert(converter)
[docs] class exc(code): """ The name of an exception class. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.classname(self.content) return e.convert(converter)
[docs] class markup(code): """ A string of formatting markup in text that is to be represented literally. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.markup(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="markup") return e.convert(converter)
[docs] class self(code): """ Use this class when referring to the object for which a method has been called, e.g.:: this function fooifies the object <self/>;. """ xmlns = xmlns model = sims.Empty() def convert_docbook(self, converter): e = converter.target.varname("self") return e.convert(converter) def convert_html(self, converter): e = converter.target.code("self", class_="self") return e.convert(converter) def convert_fo(self, converter): e = converter.target.inline("self", converter[self].codeattrs) return e.convert(converter)
self_ = self
[docs] class cls(inline): """ Use this class when referring to the object for which a class method has been called, e.g.:: this function fooifies the class <cls/>. """ xmlns = xmlns model = sims.Empty() def convert_docbook(self, converter): e = converter.target.varname("cls") return e.convert(converter) def convert_html(self, converter): e = converter.target.code("cls", class_="cls") return e.convert(converter) def convert_fo(self, converter): e = converter.target.inline("cls", converter[self].codeattrs) return e.convert(converter)
[docs] class obj(code): """ A object of unspecified type. """ xmlns = xmlns model = sims.ElementsOrText(rep, self, cls) def convert_docbook(self, converter): e = converter.target.varname(self.content) return e.convert(converter)
[docs] class mod(code): """ The name of a Python module. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.classname(self.content, role="module") return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="module") return e.convert(converter)
[docs] class file(code): """ The name of a file. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.filename(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.code(self.content, class_="filename") return e.convert(converter)
[docs] class dir(code): """ The name of a directory. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.filename(self.content, class_="directory") return e.convert(converter)
[docs] class user(code): """ The name of a user account. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.literal(self.content, role="username") return e.convert(converter)
[docs] class host(code): """ The name of a computer. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.literal(self.content, role="hostname") return e.convert(converter)
[docs] class const(code): """ The name of a constant. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.literal(self.content, role="constant") return e.convert(converter)
[docs] class data(code): """ The name of a data object. """ xmlns = xmlns model = sims.ElementsOrText(rep) def convert_docbook(self, converter): e = converter.target.literal(self.content, role="data") return e.convert(converter)
[docs] class app(inline): """ The name of a software program. """ xmlns = xmlns model = sims.ElementsOrText(rep) class Attrs(xsc.Element.Attrs): class moreinfo(xsc.URLAttr): pass def convert_docbook(self, converter): e = converter.target.application(self.content, moreinfo=self.attrs.moreinfo) return e.convert(converter) def convert_html(self, converter): if "moreinfo" in self.attrs: e = converter.target.a(self.content, class_="app", href=self.attrs.moreinfo) else: e = converter.target.span(self.content, class_="app") return e.convert(converter) def convert_fo(self, converter): if "moreinfo" in self.attrs: e = converter.target.basic_link( self.content, converter[self].linkattrs, external_destination=self.attrs.moreinfo ) else: e = self.content return e.convert(converter)
[docs] class h(base): """ The text of the title of a :class:`section` or an :class:`example`. """ xmlns = xmlns model = sims.ElementsOrText(inline) def convert_docbook(self, converter): e = converter.target.title(self.content.convert(converter)) return e.convert(converter) def convert_html(self, converter): context = converter[self] if context.stack: if isinstance(context.stack[-1], example): e = self.content elif isinstance(context.stack[-1], section): level = len(context.sections) if context.firstheaderlevel is None: context.firstheaderlevel = level e = getattr(converter.target, f"h{context.firstheaderlevel+level}", converter.target.h6)(self.content) else: raise ValueError(f"unknown node {context.stack[-1]!r} on the stack") else: context.firstheaderlevel = 0 e = converter.target.h1(self.content) return e.convert(converter) def convert_fo(self, converter): e = self.content return e.convert(converter)
[docs] class section(block): """ A recursive section. """ xmlns = xmlns model = sims.Elements(h, block) class Attrs(xsc.Element.Attrs): class role(xsc.TextAttr): pass class id(xsc.IDAttr): pass def convert_docbook(self, converter): e = converter.target.section(self.content, role=self.attrs.role, id=self.attrs.id) return e.convert(converter) def convert_html(self, converter): target = converter.target context = converter[self] context.sections[-1] += 1 level = len(context.sections) context.sections.append(0) # for numbering the subsections ts = xsc.Frag() cs = html.div(class_="content") for child in self: if isinstance(child, h): ts.append(child) else: cs.append(child) e = target.div(class_=("section level", level), id=self.attrs.id) if "role" in self.attrs: e.attrs.class_.append(" ", self.attrs.role) #if "id" in self.attrs: # e.append(target.a(name=self.attrs.id, id=self.attrs.id)) hclass = getattr(target, f"h{level}", target.h6) for t in ts: e.append(hclass(t.content)) e.append(cs) with _stack(context, self): # make sure to call the inner convert() before popping the number off of the stack e = e.convert(converter) del context.sections[-1] return e def convert_fo(self, converter): context = converter[self] context.sections[-1] += 1 context.sections.append(0) ts = xsc.Frag() cs = xsc.Frag() props = [ # size, before, after ("30pt", "30pt", "2pt"), ("22pt", "20pt", "2pt"), ("16pt", "15pt", "2pt"), ("12pt", "15pt", "2pt") ] for child in self.content: if isinstance(child, h): ts.append(child.content) else: cs.append(child) p = props[min(len(context.sections)-1, len(props)-1)] isref = str(self.attrs.role.convert(converter)) in ("class", "method", "property", "function", "module") number = None if isref: context.indentcount += 1 text_indent = context.dedent() else: if len(context.sections)>1: number = ( ".".join(str(s) for s in context.sections[:-1]), ". " ) text_indent = None tattrs = fo.block.Attrs( font_size=p[0], color=context.llblue, space_before=p[1], space_after=p[2], text_align="left", font_family=context.hdfont, keep_with_next_within_page="always", text_indent=text_indent ) e = fo.block( fo.block(number, ts, tattrs), cs, start_indent=context.indent() ) e = e.convert(converter) del context.sections[-1] if isref: context.indentcount -= 1 return e
[docs] class p(block): """ A paragraph. """ xmlns = xmlns model = sims.ElementsOrText(inline) class Attrs(xsc.Element.Attrs): class type(xsc.TextAttr): pass def convert_docbook(self, converter): e = converter.target.para(self.content, role=self.attrs.type) return e.convert(converter) def convert_html(self, converter): e = converter.target.p(self.content, class_=self.attrs.type) return e.convert(converter) def convert_fo(self, converter): e = fo.block( self.content, converter[self].vspaceattrs, line_height="130%" ) return e.convert(converter)
[docs] class dt(block): """ A term inside a :class:`dl`. """ xmlns = xmlns model = sims.ElementsOrText(inline) def convert_docbook(self, converter): e = converter.target.term(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.dt(self.content) return e.convert(converter) def convert_fo(self, converter): e = converter.target.block( self.content, font_style="italic" ) return e.convert(converter)
[docs] class li(block): """ A wrapper for the elements of a list item in :class:`ul` or :class:`ol`. """ xmlns = xmlns model = sims.ElementsOrText(block, inline) # if it contains no block elements, the content will be promoted to a paragraph def convert_docbook(self, converter): if self[block]: content = self.content else: content = converter.target.para(self.content) e = converter.target.listitem(content) return e.convert(converter) def convert_html(self, converter): e = converter.target.li(self.content) return e.convert(converter) def convert_fo(self, converter): target = converter.target context = converter[self] context.lists[-1][1] += 1 type = context.lists[-1][0] if type=="ul": label = "\u2022" elif type=="ol": label = f"{context.lists[-1][1]}." context.indentcount += 1 if self[block]: # Do we have a block in our content? content = self.content # yes => use the content as is else: content = p(self.content) # no => wrap it in a paragraph e = target.list_item( target.list_item_label( target.block(label), start_indent=context.labelindent() ), target.list_item_body( content, start_indent=context.indent() ) ) context.indentcount -= 1 return e.convert(converter)
[docs] class dd(block): """ A wrapper for the elements of a list item :class:`dl`. """ xmlns = xmlns model = sims.ElementsOrText(block, inline) # if it contains no block elements, the content will be promoted to a paragraph def convert_docbook(self, converter): if self[block]: content = self.content else: content = converter.target.para(self.content) e = converter.target.listitem(content) return e.convert(converter) def convert_html(self, converter): e = converter.target.dd(self.content) return e.convert(converter) def convert_fo(self, converter): target = converter.target context = converter[self] context.lists[-1][1] += 1 type = context.lists[-1][0] context.indentcount += 1 if self[block]: # Do we have a block in our content? content = self.content # yes => use the content as is else: content = p(self.content) # no => wrap it in a paragraph e = target.block( content, start_indent=context.indent() ) context.indentcount -= 1 return e.convert(converter)
[docs] class list(block): """ Common baseclass for :class:`ul`, :class:`ol` and :class:`dl`. """ xmlns = xmlns register = False
[docs] class ul(list): """ A list in which each entry is marked with a bullet or other dingbat. """ xmlns = xmlns model = sims.Elements(li) def convert_docbook(self, converter): e = converter.target.itemizedlist(self.content.convert(converter)) return e.convert(converter) def convert_html(self, converter): with _stack(converter[self], self): return converter.target.ul(self.content.convert(converter)) def convert_fo(self, converter): context = converter[self] context.lists.append(["ul", 0]) e = converter.target.list_block(self.content, line_height="130%") e = e.convert(converter) del context.lists[-1] return e
[docs] class ol(list): """ A list in which each entry is marked with a sequentially incremented label. """ xmlns = xmlns model = sims.Elements(li) def convert_docbook(self, converter): e = converter.target.orderedlist(self.content.convert(converter)) return e.convert(converter) def convert_html(self, converter): with _stack(converter[self], self): return converter.target.ol(self.content.convert(converter)) def convert_fo(self, converter): context = converter[self] context.lists.append(["ol", 0]) e = converter.target.list_block(self.content, line_height="130%") e = e.convert(converter) del context.lists[-1] return e
[docs] class dl(list): """ A list in which each entry is marked with a label. """ xmlns = xmlns model = sims.Elements(dt, dd) def convert_docbook(self, converter): e = converter.target.variablelist() collect = converter.target.varlistentry() for child in self.content: collect.append(child) if isinstance(child, dd): e.append(collect) collect = converter.target.varlistentry() if collect: e.append(collect) return e.convert(converter) def convert_html(self, converter): with _stack(converter[self], self): return converter.target.dl(self.content.convert(converter)) def convert_fo(self, converter): context = converter[self] context.lists.append(["dl", 0]) e = self.content.convert(converter) del context.lists[-1] return e
[docs] class example(block): """ A formal example. """ xmlns = xmlns model = sims.Elements(h, block) def convert_docbook(self, converter): e = converter.target.example(self.content) return e.convert(converter) def convert_html(self, converter): target = converter.target ts = xsc.Frag() e = xsc.Frag() for child in self: if isinstance(child, h): ts.append(child) else: e.append(child) if ts: e.append(target.div(ts, class_="example-title")) with _stack(converter[self], self): return e.convert(converter) def convert_fo(self, converter): # FIXME: handle title e = xsc.Frag() for child in self.content: if not isinstance(child, h): e.append(child) return e.convert(converter)
[docs] class a(inline): """ A hypertext link. """ xmlns = xmlns model = sims.ElementsOrText(inline) class Attrs(xsc.Element.Attrs): class href(xsc.URLAttr): pass class hreflang(xsc.TextAttr): pass def convert_docbook(self, converter): e = converter.target.link(self.content, linkend=self.attrs.href) return e.convert(converter) def convert_html(self, converter): e = converter.target.a(self.content, href=self.attrs.href, hreflang=self.attrs.hreflang) return e.convert(converter) def convert_fo(self, converter): if "href" in self.attrs: e = converter.target.basic_link( self.content, converter[self].linkattrs, external_destination=self.attrs.href ) else: e = self.content return e.convert(converter)
[docs] class xref(inline): """ An internal cross reference. """ xmlns = xmlns model = sims.ElementsOrText(inline) class Attrs(xsc.Element.Attrs): class ref(xsc.TextAttr): pass def convert_docbook(self, converter): e = converter.target.link(self.content, linkend=self.attrs.ref) return e.convert(converter) def convert_html(self, converter): e = converter.target.a(self.content, href=("#", self.attrs.ref)) return e.convert(convertert) def convert_fo(self, converter): if "href" in self.attrs: e = converter.target.basic_link( self.content, converter[self].linkattrs, internal_destination=self.attrs.ref ) else: e = self.content return e.convert(converter)
[docs] class email(inline): """ An email address. """ xmlns = xmlns model = sims.NoElements() def convert_docbook(self, converter): e = converter.target.email(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.a(self.content, href=("mailto:", self.content)) return e.convert(converter) def convert_fo(self, converter): e = converter.target.basic_link( self.content, converter[self].linkattrs, external_destination=("mailto:", self.content) ) return e.convert(converter)
[docs] class em(inline): """ Emphasized text. """ xmlns = xmlns model = sims.ElementsOrText(inline) def convert_docbook(self, converter): e = converter.target.emphasis(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.em(self.content) return e.convert(converter) def convert_fo(self, converter): e = converter.target.inline( self.content, converter[self].emattrs ) return e.convert(converter)
[docs] class strong(inline): """ Emphasized text. """ xmlns = xmlns model = sims.ElementsOrText(inline) def convert_docbook(self, converter): e = converter.target.emphasis(self.content) return e.convert(converter) def convert_html(self, converter): e = converter.target.strong(self.content) return e.convert(converter) def convert_fo(self, converter): e = converter.target.inline( self.content, converter[self].strongattrs ) return e.convert(converter)
[docs] class z(inline): """ Put the content into double quotes. """ xmlns = xmlns model = sims.ElementsOrText(inline) def convert(self, converter): e = xsc.Frag("\u201c", self.content, "\u201d") return e.convert(converter)
[docs] class pyref(inline): """ Reference to a Python object: module, class, method, property or function. """ xmlns = xmlns model = sims.ElementsOrText(inline) class Attrs(xsc.Element.Attrs): class module(xsc.TextAttr): pass class class_(xsc.TextAttr): xmlname = "class" class method(xsc.TextAttr): pass class property(xsc.TextAttr): pass class function(xsc.TextAttr): pass class Context(xsc.Element.Context): def __init__(self): xsc.Element.Context.__init__(self) self.base = "http://127.0.0.1:7464/" def convert(self, converter): target = converter.target context = converter[self] if target.xmlns == xmlns: # our own namespace return self.convert_doc(converter) if "function" in self.attrs: function = str(self.attrs.function.convert(converter)) else: function = None if "method" in self.attrs: method = str(self.attrs.method.convert(converter)) else: method = None if "property" in self.attrs: prop = str(self.attrs.property.convert(converter)) else: prop = None if "class" in self.attrs: class_ = str(self.attrs.class_.convert(converter)).replace(".", "-") else: class_ = None if "module" in self.attrs: module = str(self.attrs.module.convert(converter)) if module.startswith("ll."): module = module[3:].replace(".", "/") elif module == "ll": module = "core" else: module = None else: module = None e = self.content if target.xmlns == html.xmlns: if function is not None: if module is not None: e = target.a(e, href=(context.base, module, "/index.html#", function)) elif method is not None: if class_ is not None and module is not None: e = target.a(e, href=(context.base, module, "/index.html#", class_, "-", method)) elif prop is not None: if class_ is not None and module is not None: e = target.a(e, href=(context.base, module, "/index.html#", class_, "-", prop)) elif class_ is not None: if module is not None: e = target.a(e, href=(context.base, module, "/index.html#", class_)) elif module is not None: e = target.a(e, href=(context.base, module, "/index.html")) return e.convert(converter)
class fodoc(base): xmlns = xmlns model = sims.Elements(block) def convert(self, converter): context = converter[self] e = self.content converter.push(target=sys.modules[__name__]) # our own module e = e.convert(converter) converter.pop() converter.push(target=fo) e = e.convert(converter) converter.pop() e = xsc.Frag( xml.XML(), "\n", fo.root( fo.layout_master_set( fo.simple_page_master( fo.region_body( region_name="xsl-region-body", margin_bottom="3cm" ), fo.region_after( region_name="xsl-region-after", extent="2cm" ), master_name="default", page_height="29.7cm", page_width="21cm", margin_top="1cm", margin_bottom="1cm", margin_left="2.5cm", margin_right="1cm" ) ), fo.page_sequence( fo.static_content( fo.block( fo.page_number(), border_before_width="0.1pt", border_before_color="#000", border_before_style="solid", padding_before="4pt", text_align="center" ), flow_name="xsl-region-after" ), fo.flow( e, flow_name="xsl-region-body" ), master_reference="default" ), font_family=context.font, font_size="10pt", text_align="justify", line_height="normal", language="en", orphans=2, widows=3 ) ) return e