2. Sphinx

Am Anfang (ca. 2002) war die Markup Sprache reStructuredText (reST). Das war eine Idee aus der Welt der Programmiersprache Python, um Dokumentation zu schreiben, die automatisch mit Hilfe von Umwandlungsprogrammen in Ausgabeformate wie HTML und PDF umgewandelt werden kann. Die Programme sind im Linux Paket docutils enthalten, man installiert sie mit apt install python3-docutils (es ist immer Python3 zu bevorzugen, da Python 2 im Jahr 2020 eingestellt wurde). Wenn man eine Textdatei text.rst mit reStructuredText (Endung .rst) geschrieben hat, dann wandelt man diese nach HTML um mit dem Kommando:

rst2html text.rst text.html

Die Homepage der „docutils“ ist hier https://docutils.sourceforge.io/rst.html

Mit dem Text Quick reStructuredText kann man sich schnell einen Überblick über die wichtigsten Markup Befehle verschaffen:

Eine wichtige Eigenschaft von reST ist seine Erweiterbarkeit. Neue Markup-Befehle können einfach definiert werden.

Das Sphinx Projekt hat diese Erweiterbarkeit genutzt und reST zu einer kompletten Textverarbeitung weiterentwickelt, mit der man beliebige Dokumente erstellen kann.

_images/sphinx-logo.png

Abb. 2.1 Die Homepage von Sphinx ist https://www.sphinx-doc.org.

Sphinx gibt es im Paket-Repository ihres Linux Rechners unter python3-sphinx. Man kann Sphinx auch vom Python Package Index installieren mit dem Kommando pip install -U Sphinx.

Um eine Dokumentationsprojekt zu beginnen, legt man ein neues Unterverzeichnis an, z.B. dva-bericht, und ruft darin das Programm sphinx-quickstart auf. Nach ein paar ja/nein Abfragen (siehe Log) wird dadurch das Gerüst einer neuen Dokumentation generiert. Danach sind in dem Verzeichnis folgende Dateien:

dva-bericht/
   conf.py      <-- Konfiguration fuer Sphinx
   index.rst    <-- Die Hauptseite
   make.bat     <-- Zum Bauen der Doku unter Windows
   Makefile     <-- Zum Bauen der Doku unter Linux
   _static      <-- Hilfsverzeichnis, kann man ignorieren
   _templates   <-- Hilfsverzeichnis, kann man ignorieren

Mit sphinx-quickstart kann man auch herausfinden, welche Version von Sphinx man installiert hat, auf meinem Rechner erhalte ich folgende Ausgabe (die Version kann bei Euch eine andere sein):

$ sphinx-quickstart --version
sphinx-quickstart 6.1.3

Die „richtige“ Dokumentation in einem bestimmten Format muss erst erzeugt werden. Wenn man z.B. nach HTML ausgeben möchte, muss man eingeben: make html. Wenn man make ohne Option eingibt, bekommt man eine Liste aller Formate. Darunter sind html, latex, epub, man, texinfo, text, xml und viele weitere. Die erzeugten HTML Dateien findet man in _build/html. Zum Betrachten ruft man einen Web-Browser wie folgt auf:

firefox _build/html/index.html

Sollte das Dokument aus mehren Dateien bestehen, dann werden diese in index.rst inkludiert. Dieser Text besteht z.B. aus folgenden .rst Dateien: index.rst, schreibtipps.rst, sphinx.rst und werkzeuge.rst. Zum Inkludieren werden die drei Dateien ohne .rst Endung in index.rst aufgenommen:

.. toctree::
   :maxdepth: 2
   :numbered:     <-- die Abschnitte werden nummeriert

   intro          <--- intro.rst
   sphinx
   schreibtipps
   werkzeuge
   anpassen
   schluss

2.1. Überschriften

Die Überschriften kann man in verschiedenen Ebenen anlegen:

Ebene 1    <== Grosse Ueberschrift am Anfang in jeder .rst Datei
=======

Ebene 2
-------

Ebene 3
```````

Ebene 4
.......

2.1.1. Test Ebene 3

Die Nummerierung sollte 2.1.1 sein.

2.1.1.1. Test Ebene 4

Die Nummerierung sollte 2.1.1.1 sein. Mehr Ebenen braucht man nicht!

2.2. Querverweise

Man kann ganz einfach auf andere Abschnitte verweisen, wenn man vor dem Abschnitt mit:

.. _xyz:

ein „Label“ setzt. In diesem Fall hat das Label den Namen xyz. Vor den obigen Abschnitt „Überschriften“ habe ich das Label:

.. _ueberschriften:

gesetzt, somit kann ich nun auf diesen Abschnitt mit :numref:`ueberschrift` verweisen, der Link geht dann zu Abschnitt 2.1.

Ein anderer Link: In Abschnitt 2.5 kann man nachlesen, wie man Formeln in Sphinx schreibt. Das wurde gemacht durch :numref:`formeln`.

2.3. Ein wenig Markup

In Sphinx wird der in reStructuredText definierte Markup verwendet, plus zusätzlicher Markup, der nur in Sphinx existiert. Hier sind ein paar Beispiele:

Wörter können in verschiedenen Schriftarten geschrieben werden: fett, kursiv, Schreibmaschine.

Immer gut zu gebrauchen sind Postenlisten oder Aufzählungslisten:

  • Mikroprozessoren

    • STM32L476

    • AVR Mega32U4

  • Spannungsregler

    • 78L05

  • Widerstände

    • 470 Ω

    • 2,2 kΩ

Bei den Listen immer aufpassen, dass vor dem Beginn einer neuen Einrückung eine Leerzeile gemacht wird.

Eine Aufzählung:

  1. Aufstehen

  2. Zähne putzen

  3. Frühstücken

Wie man in der .rst Datei sieht, kann man ab dem zweiten Punkt die Zahl weglassen und stattdessen einfach #. schreiben.

Sehr vielseitig zu verwenden sind auch Code-Blöcke. Das sind Abschnitte, die in „Computerschrift“ gesetzt werden. Ein Beispiel:

Zeile 1
Zeile 2
Zeile 3

In der .rst Datei habe ich dazu geschrieben:

.. code-block:: text
   Zeile 1
   Zeile 2
   Zeile 3

Die Angabe hinter code-block:: bestimmt die Syntax, in der der folgende Abschnitt hervorgehoben wird. Mit text erfolgt keine Hervorhebung. Andere Möglichkeiten sind hier z.B. c, python, java, bash und so weiter.

Hier sind ein paar Beispiele:

/* c */
int main()
{
    printf("Hello World\n");
    return 0;
}

# python
L = []
for i in range(10):
   L.append(i)
   print(i)

# bash
if [ -f "/home/hhoegl/miniconda3/etc/profile.d/conda.sh" ]; then
     . "/home/hhoegl/miniconda3/etc/profile.d/conda.sh"
     CONDA_CHANGEPS1=false conda activate base
 else
     export PATH="/home/hhoegl/miniconda3/bin:$PATH"
 fi

Kommentare schreibt man in die .rst Dateien wie folgt:

.. Das ist ein Kommentar. Es erfolgt
   keine Ausgabe.

Alles weitere kann man der Sphinx Homepage ([SPHINX]) oder einem guten Tutorial entnehmen, z.B. [SPHTUT].

2.4. Programmtexte

Häufig müssen Sie Quelltextabschnitte in die Dokumentation aufnehmen. Sie können die Code-Blöcke entweder direkt in die .rst Datei aufnehmen oder aus einer Datei lesen:

Hier ist ein Beispiel für einen Codeabschnitt in Python. Man kann einen Codeabschnitt auch mit einer Über- bzw. Unterschrift versehen („caption“) und eine Referenz drauf setzen mit „numref“ (siehe Codeabschnitt 2.4.1):

Codeabschnitt 2.4.1 „Hello World“ in Python
# hallo.py
def hallo():
    print("Hallo Welt")
hallo()

und hier ist ein Abschnitt in C. Die Zeilennummern werden durch :linenos: aktiviert. Man kann auch den Beginn und die Schrittweite der Zeilennummern einstellen.

1/* hallo.c */
2#include <stdio.h>
3
4int main()
5{
6    printf("Hallo Welt\n");
7    return(0);
8}

Wenn man den Quelltext aus einer Datei lesen möchte, dann geht das so (siehe die Datei serial.c):

.. literalinclude:: serial.c
   :caption: C Code zum Ansprechen der seriellen Schnittstelle
   :name: serialcode
   :language: c
   :linenos:

Hier ist die Ausgabe:

Codeabschnitt 2.4.2 C Code zum Ansprechen der seriellen Schnittstelle
  1#include <errno.h>
  2#include <fcntl.h> 
  3#include <stdio.h>
  4#include <stdlib.h>
  5#include <string.h>
  6#include <termios.h>
  7#include <unistd.h>
  8
  9int set_interface_attribs(int fd, int speed)
 10{
 11    struct termios tty;
 12
 13    if (tcgetattr(fd, &tty) < 0) {
 14        printf("Error from tcgetattr: %s\n", strerror(errno));
 15        return -1;
 16    }
 17
 18    cfsetospeed(&tty, (speed_t)speed);
 19    cfsetispeed(&tty, (speed_t)speed);
 20
 21    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
 22    tty.c_cflag &= ~CSIZE;
 23    tty.c_cflag |= CS8;         /* 8-bit characters */
 24    tty.c_cflag &= ~PARENB;     /* no parity bit */
 25    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
 26    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */
 27
 28    /* setup for non-canonical mode */
 29    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
 30    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
 31    tty.c_oflag &= ~OPOST;
 32
 33    /* fetch bytes as they become available */
 34    tty.c_cc[VMIN] = 1;
 35    tty.c_cc[VTIME] = 1;
 36
 37    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
 38        printf("Error from tcsetattr: %s\n", strerror(errno));
 39        return -1;
 40    }
 41    return 0;
 42}
 43
 44void set_mincount(int fd, int mcount)
 45{
 46    struct termios tty;
 47
 48    if (tcgetattr(fd, &tty) < 0) {
 49        printf("Error tcgetattr: %s\n", strerror(errno));
 50        return;
 51    }
 52
 53    tty.c_cc[VMIN] = mcount ? 1 : 0;
 54    tty.c_cc[VTIME] = 5;        /* half second timer */
 55
 56    if (tcsetattr(fd, TCSANOW, &tty) < 0)
 57        printf("Error tcsetattr: %s\n", strerror(errno));
 58}
 59
 60
 61int main()
 62{
 63    char *portname = "/dev/ttyUSB0";
 64    int fd;
 65    int wlen;
 66
 67    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
 68    if (fd < 0) {
 69        printf("Error opening %s: %s\n", portname, strerror(errno));
 70        return -1;
 71    }
 72    /*baudrate 115200, 8 bits, no parity, 1 stop bit */
 73    set_interface_attribs(fd, B115200);
 74    //set_mincount(fd, 0);                /* set to pure timed read */
 75
 76    /* simple output */
 77    wlen = write(fd, "Hello!\n", 7);
 78    if (wlen != 7) {
 79        printf("Error from write: %d, %d\n", wlen, errno);
 80    }
 81    tcdrain(fd);    /* delay for output */
 82
 83
 84    /* simple noncanonical input */
 85    do {
 86        unsigned char buf[80];
 87        int rdlen;
 88
 89        rdlen = read(fd, buf, sizeof(buf) - 1);
 90        if (rdlen > 0) {
 91#ifdef DISPLAY_STRING
 92            buf[rdlen] = 0;
 93            printf("Read %d: \"%s\"\n", rdlen, buf);
 94#else /* display hex */
 95            unsigned char   *p;
 96            printf("Read %d:", rdlen);
 97            for (p = buf; rdlen-- > 0; p++)
 98                printf(" 0x%x", *p);
 99            printf("\n");
100#endif
101        } else if (rdlen < 0) {
102            printf("Error from read: %d: %s\n", rdlen, strerror(errno));
103        }
104        /* repeat read to get full message */
105    } while (1);
106}

Auch bei literalcode kann man eine Bildüberschrift setzen und eine Referenz angeben mit numref (siehe Codeabschnitt 2.4.2).

Nun noch ein Beispiel, das zeigt, wie Sie einen Link auf eine Quelltextdatei setzen. Im Verzeichnis sphinxbericht gibt es die Datei hello_world.py. Es funktioniert nicht, wenn man die Datei im aktuellen Verzeichnis verlinkt:

Der Grund ist, dass die Quelltextdatei nicht in den Ausgabeordner _build/html kopiert wird.

Die Datei sollte in den Ordner _static kopiert werden, dieser Ordner wird komplett in das Ausgabeverzeichnis kopiert.

2.5. Formeln schreiben

Formeln schreiben ist in Sphinx einfach: Man verwendet die gleiche Syntax wie in Latex!

Beispiel (Pythagoras): \(a^2 + b^2 = c^2\).

Die folgende Formel

(2.5.1)\[ \begin{align}\begin{aligned}(a + b)^2 = a^2 + 2ab + b^2\\(a - b)^2 = a^2 - 2ab + b^2\end{aligned}\end{align} \]

wird so geschrieben:

.. math::

   (a + b)^2 = a^2 + 2ab + b^2

   (a - b)^2 = a^2 - 2ab + b^2

Damit man math verwenden kann, muss man die Sphinx Erweiterung sphinx.ext.mathjax in die Variable extensions in conf.py aufnehmen.

2.6. Literatur

[SPHINX]

Die Sphinx Homepage (besucht am 13.3.2023): https://www.sphinx-doc.org

[SPHTUT]

Thomas Cokelaer, Sphinx and RST syntax guide, 2014 (besucht am 13.3.2023) https://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html