Diese Dokumentation wurde zur Beschreibung der Serie 1.7.x von Apache™ Subversion® erstellt. Falls Sie eine unterschiedliche Version von Subversion einsetzen, sei Ihnen dringend angeraten, bei http://www.svnbook.com/ vorbeizuschauen und stattdessen die zu Ihrer Version von Subversion passende Version dieser Dokumentation heranzzuiehen.

Benutzung der APIs

Es ist ziemlich unkompliziert, Anwendungen mit den APIs der Bibliotheken von Subversion zu entwickeln. Subversion ist vor allem eine Menge aus C-Bibliotheken mit Header-Dateien (.h), die im Verzeichnis subversion/include des Quelltextbaums liegen. Diese Header werden in Ihre Systemverzeichnisse (z.B. /usr/local/include) kopiert, wenn Sie Subversion aus den Quellen bauen und installieren. Diese Header repräsentieren die Gesamtheit der Funktionen und Typen, die den Benutzern der Subversion-Bibliotheken zugänglich gemacht werden sollen. Die Entwicklergemeinde von Subversion achtet peinlich genau darauf, dass die öffentliche API gut dokumentiert ist – diese Dokumentation finden Sie direkt in den Header-Dateien.

Beim Untersuchen der öffentlichen Header-Dateien wird Ihnen zunächst auffallen, dass die Datentypen und Funktionen von Subversion durch Namensräume geschützt sind. Das heißt, jeder öffentliche Symbolname beginnt mit svn_, gefolgt von einem Kürzel der Bibliothek, in der das Symbol definiert ist (etwa wc, client, fs usw.), gefolgt von einem einzelnen Unterstrich (_) und dem Rest des Symbolnamens. Halböffentliche Funktionen (die zwischen Quelldateien einer Bibliothek, jedoch nicht außerhalb davon verwendet werden und innerhalb der Bibliotheksverzeichnisse zu finden sind) weichen von diesem Namensschema ab, indem statt eines einzelnen Unterstrichs nach dem Bibliothekskürzel zwei Unterstriche stehen (_ _). Private Funktionen in einer Quelldatei haben keinen besonderen Präfix und werden static deklariert. Einem Compiler sind diese Konventionen natürlich egal, doch sie helfen, den Gültigkeitsbereich einer gegebenen Funktion oder eines Datentypen deutlich zu machen.

Eine weitere gute Informationsquelle zur Programmierung mit den Subversion-APIs sind die Programmierrichtlinien des Projektes, die Sie unter http://subversion.apache.org/docs/community-guide/ finden können. Dieses Dokument enthält nützliche Informationen, die, obwohl sie für Entwickler und angehende Entwickler von Subversion selbst gedacht sind, genauso für Leute geeignet sind, die mit Subversion als ein Satz von Bibliotheken eines Drittanbieters entwickeln. [66]

Die Bibliothek Apache Portable Runtime

Neben den eigenen Datentypen von Subversion werden Sie viele Verweise auf Datentypen entdecken, die mit apr_ beginnen – Symbole aus der Bibliothek Apache Portable Runtime (APR). APR ist die Portabilitätsbibliothek von Apache, ursprünglich aus dem Server-Code herausgelöst, als ein Versuch, die betriebssystemspezifischen Teile von den betriebssystemabhängigen Bereichen des Codes zu trennen. Das Ergebnis war eine Bibliothek, die ein generisches API für das Ausführen von Operationen bietet, die sich je nach Betriebssystem mehr oder weniger stark unterscheiden. Obwohl der Apache HTTP-Server offensichtlich die APR-Bibliothek als erster verwendete, erkannten die Subversion-Entwickler sofort die Vorteile, die durch die Benutzung von APR entstehen. Das bedeutet, dass es praktisch keinen betriebssystemspezifischen Code in Subversion gibt. Es bedeutet auch, dass der Subversion-Client sich auf jedem System übersetzen und betreiben lässt, auf dem das auch für den Apache HTTP-Server gilt. Momentan umfasst diese Liste alle Dialekte von Unix, Win32, BeOS, OS/2 und Mac OS X.

Neben der Bereitstellung konsistenter Implementierungen von Systemaufrufen, die sich zwischen Betriebssystemen unterscheiden,[67] bietet APR Subversion unmittelbaren Zugriff auf viele maßgeschneiderte Datentypen, wie etwa dynamische Arrays und Hashtabellen. Subversion macht von diesen Typen regen Gebrauch. Der vielleicht am meisten verbreitete APR-Datentyp, der sich in beinahe jedem Subversion-API-Prototypen wiederfindet, ist apr_pool_t — der APR-Speicherpool. Subversion verwendet Pools intern zum Zuteilen all seines Speichers (außer wenn eine externe Bibliothek eine unterschiedliche Speicherverwaltung für die über ihre API ausgetauschten Daten voraussetzt),[68] und obwohl jemand, der mit den Subversion-APIs programmiert nicht gezwungen ist, dasselbe zu tun, muss er Pools für die API-Funktionen zur Verfügung stellen, die sie benötigen. Das heißt, dass Benutzer der Subversion-API auch gegen die APR linken, apr_initialize() zum Initialisieren des APR-Subsystems aufrufen und Pools zur Verwendung mit Subversion-API-Aufrufen erzeugen sowie verwalten müssen, typischerweise unter Benutzung von svn_pool_create(), svn_pool_clear() und svn_pool_destroy().

Funktionen und Batons

Um ein datenstromähnliches (asynchrones) Verhalten zu ermöglichen und Benutzern der Subversion C-Programmierschnittstelle Anknüpfpunkte für eine flexible Informationsverarbeitung zur Verfügung zu stellen, akzeptieren viele Funktionen der Schnittstelle Parameterpaare: ein Zeiger auf eine Rückruffunktion und einen Zeiger auf einen Speicherbereich, genannt Baton (Staffelstab), der Kontextinformationen für die Rückruffunktion beinhaltet. Bei Batons handelt es sich üblicherweise um C-Strukturen mit zusätzlichen Informationen, die die Rückruffungktion benötigt, ihr jedoch nicht direkt durch die steuernde Schnittstellenfunktion übergeben werden.

URL- und Pfadanforderungen

Da das Betreiben entfernte Versionskontrolle der Grund für das Vorhandensein von Subversion ist, ergibt es einen Sinn, dass der Internationalisierung (i18n) etwas Aufmerksamkeit gewidmet wurde. Schließlich kann entfernt sowohl am anderen Ende vom Büro als auch am anderen Ende der Welt bedeuten. Um das zu ermöglichen, erwarten alle öffentlichen Schnittstellen von Subversion, die Pfad-Argumente annehmen, dass diese Pfade im kanonischen Format vorliegen – was am besten gelingt, wenn sie durch die Funktion svn_dirent_canonicalize() oder svn_uri_canonicalize() geschickt werden (abhängig davon, ob Sie einen lokalen Systempfad bzw. einen URL in das kanonische Format umwandeln möchten) – und in UTF-8 kodiert sind. Das bedeutet beispielsweise, dass ein neuer Client, der die Schnittstelle libsvn_client benutzt, zunächst Pfade aus der sprachabhängigen Kodierung nach UTF-8 konvertieren muss, bevor sie an die Subversion-Bibliotheken übergeben werden und anschließend umgekehrt etwaige Ergebnispfade von Subversion zurück in die sprachabhängige Kodierung umgewandelt werden müssen, bevor die Pfade für Zwecke verwendet werden, die nichts mit Subversion zu tun haben. Glücklicherweise stellt Subversion eine Reihe von Funktionen zur Verfügung (siehe subversion/include/svn_utf.h) die jedes Programm zum Konvertieren benutzen kann.

Ferner ist es für die Subversion APIs erforderlich, dass alle URL-Parameter richtig URI-kodiert sind. So sollten Sie statt file:///home/username/My File.txt als URL einer Datei namens My File.txt file:///home/username/My%20File.txt übergeben. Auch hierfür stellt Subversion Hilfsfunktionen für Ihre Anwendung zur Verfügung – svn_path_uri_encode() und svn_path_uri_decode() zum Kodieren bzw. Dekodieren von URIs.

Verwendung anderer Sprachen als C und C++

Falls Sie daran interessiert sein sollten, die Subversion-Bibliotheken in Verbindung mit etwas anderem als ein C-Programm zu benutzen – etwa ein Python- oder ein Perl-Script – bietet Subversion etwas Unterstützung über den Simplified Wrapper and Interface Generator (SWIG). Die SWIG-Bindungen für Subversion liegen in subversion/bindings/swig. Sie reifen zwar noch, können aber verwendet werden. Diese Bindungen erlauben Ihnen, Subversion-API-Funktionen indirekt aufzurufen, indem eine Zwischenschicht verwendet wird, die die Datentypen Ihrer Skriptsprache in die Datentypen umwandeln, die von Subversions C-Bibliotheken benötigt werden.

Es wurden bedeutende Anstrengungen unternommen, um funktionierende SWIG-erzeugte Bindungen für Python, Perl und Ruby zur Verfügung zu stellen. Bis zu einem gewissen Grad kann die Arbeit zur Vorbereitung der SWIG-Schnittstellen für diese Sprachen wiederverwendet werden, wenn es darum geht, Bindungen für andere von SWIG unterstützte Sprachen zu erzeugen (unter anderem Versionen von C#, Guile, Java, MzScheme, OCaml, PHP und Tcl). Jedoch ist etwas zusätzliche Programmierarbeit für komplizierte APIs erforderlich, bei deren Übersetzung SWIG ein wenig Hilfe benötigt. Weitergehende Informationen zu SWIG finden Sie auf der Projektseite unter http://www.swig.org/.

Subversion verfügt ebenfalls über Sprachbindungen für Java. Die javahl-Bindungen (zu finden in subversion/bindings/java im Subversion-Quelltext-Baum) sind nicht SWIG-basiert sondern ein Gemisch aus Java und handgeschriebenem JNI. Javahl deckt die meisten client-seitigen Subversion-APIs ab und zielt besonders auf Implementierer Java-basierter Subversion-Clients und IDE-Integrationen ab.

Zwar gilt den Sprachbindungen von Subversion seitens der Entwickler nicht dieselbe Aufmerksamkeit wie den Subversion-Kernmodulen, doch sie sind durchaus bereit für den Einsatz. Eine Reihe von Skripten und Anwendungen, alternative graphische Subversion-Clients und andere Werkzeuge von Drittanbietern machen heute schon erfolgreich Gebrauch von den Subversion-Sprachbindungen, um ihre Subversion-Integration zustande zu bringen.

An dieser Stelle ist es erwähnenswert, dass es auch andere Optionen gibt, um Subversion-Schnittstellen in anderen Sprachen zu verwenden: alternative Subversion-Bindungen, die gar nicht aus der Subversion-Entwicklergemeinde stammen. Es gibt davon ein paar beliebte, die besonders erwähnenswert sind. Zunächst seien die PySVN-Bindungen von Barry Scott genannt (http://pysvn.tigris.org/), die eine beliebte Alternative zur Python-Bindung darstellen. PySVN rühmt sich, eine Schnittstelle zu liefern, die Python-typischer ist als die eher C-ähnlichen APIs der Subversion-eigenen Python-Bindungen. Und falls Sie eine reine Java-Implementierung suchen, wehen Sie sich SVNKit (http://svnkit.com/) an, das eine vollständige Reimplementierung von Subversion in Java ist.

Beispielcode

Beispiel 8.1, „Verwendung der Projektarchiv-Schicht“ enthält einen Codeabschnitt (in C), der einige der erörterten Konzepte veranschaulicht. Er verwendet sowohl die Projektarchiv- als auch die Dateisystemschnittstelle (was anhand der Präfixe svn_repos_ bzw. svn_fs_ der Funktionsnamen erkennbar ist), um eine neue Revision zu erzeugen, in der ein Verzeichnis hinzugefügt wird. Sie können die Verwendung des APR-Pools erkennen, der zur Speicherzuteilung herumgereicht wird. Der Code verdeutlicht auch eine etwas undurchsichtige Angelegenheit bezüglich der Fehlerbehandlung von Subversion – alle Fehler von Subversion müssen explizit behandelt werden, um Speicherlöcher zu verhindern (und vereinzelt Programmabstürze).

Beispiel 8.1. Verwendung der Projektarchiv-Schicht


/* Umwandlung eines Subversion-Fehlers in einen einfachen Boole'schen
 * Fehlerwert.
 *
 * NOTE:  Subversion-Fehler müssen zurückgesetzt werden (mit
 *        svn_error_clear()), da sie aus dem globalen Pool zugeteilt
 *        werden, ansonsten treten Speicherlöcher auf.
 */
#define INT_ERR(expr)                           \
  do {                                          \
    svn_error_t *__temperr = (expr);            \
    if (__temperr)                              \
      {                                         \
        svn_error_clear(__temperr);             \
        return 1;                               \
      }                                         \
    return 0;                                   \
  } while (0)


/* Ein neues Verzeichnis im Pfad NEW_DIRECTORY des
 * Subversion-Projektarchivs bei REPOS_PATH erzeugen. Sämtliche
 * Speicherzuteilungen in Pool durchführen. Diese Funktion erzeugt
 * eine neue Revision, damit NEW_DIRECTORY hinzugefügt werden kann. Im
 * Erfolgsfall Null, sonst einen Wert ungleich Null zurückgeben.
 */
static int
make_new_directory(const char *repos_path,
                   const char *new_directory,
                   apr_pool_t *pool)
{
  svn_error_t *err;
  svn_repos_t *repos;
  svn_fs_t *fs;
  svn_revnum_t youngest_rev;
  svn_fs_txn_t *txn;
  svn_fs_root_t *txn_root;
  const char *conflict_str;


  /* Projektarchiv bei REPOS_PATH öffnen. 
   */
  INT_ERR(svn_repos_open(&repos, repos_path, pool));


  /* Zeiger auf das Dateisystemobjekt in REPOS holen. 
   */
  fs = svn_repos_fs(repos);


  /* Anfrage beim Dateisystem nach der aktuell letzten existierenden
   * Revision.
   */
  INT_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));


  /* Starten einer neuen Transaktion basierend auf YOUNGEST_REV. Die
   * Wahrscheinlichkeit einer später wegen Konflikte abgelehnten
   * Übergabe sinkt, wenn wir stets versuchen, unsere Änderungen auf
   * der letzten Momentaufnahme des Dateisystembaums zu machen.
   */
  INT_ERR(svn_repos_fs_begin_txn_for_commit2(&txn, repos, youngest_rev,
                                             apr_hash_make(pool), pool));


  /* Nach dem Start der Transaktion wird ein Wurzelobjekt geholt, das
   * diese Transaktion repräsentiert.
   */
  INT_ERR(svn_fs_txn_root(&txn_root, txn, pool));
  

  /* Das neue Verzeichnis unter der Transaktionswurzel im Pfad
   * NEW_DIRECTORY anlegen. 
   */
  INT_ERR(svn_fs_make_dir(txn_root, new_directory, pool));


  /* Die Transaktion übergeben, indem eine neue Revision des
   * Dateisystems erzeugt wird, die unseren hinzugefügten
   * Verzeichnispfad enthält.
   */
  err = svn_repos_fs_commit_txn(&conflict_str, repos, 
                                &youngest_rev, txn, pool);
  if (! err)
    {

      /* Kein Fehler? Ausgezeichnet! Eine kurze Erfolgsnachricht
       * ausgeben.
       */

      printf("Verzeichnis '%s' ist erfolgreich als neue Revision "
             "'%ld' hinzugefügt worden.\n", new_directory, youngest_rev);
    }
  else if (err->apr_err == SVN_ERR_FS_CONFLICT)
    {

      /* Oh-ha. Die Übergabe schlug wegen eines Konfliktes fehl
       * (jemand anderes scheint im gleichen Bereich des Dateisystems
       * Änderungen gemacht zu haben, das wir ändern wollten). Eine
       * Fehlermeldung ausgeben.
       */

      printf("Ein Konflikt trat im Pfad '%s' auf, als versucht wurde, "
             "das Verzeichnis '%s' dem Projektarchiv bei '%s' hinzuzufügen.\n", 
             conflict_str, new_directory, repos_path);
    }
  else
    {

      /* Ein anderer Fehler ist aufgetreten. Eine Fehlermeldung
       * ausgeben.
       */

      printf("Beim Versuch, das Verzeichnis '%s' dem Projektarchiv bei "
             "'%s' hinzuzufügen, trat ein Fehler auf.\n", 
             new_directory, repos_path);
    }

  INT_ERR(err);
} 

Beachten Sie, dass der Code in Beispiel 8.1, „Verwendung der Projektarchiv-Schicht“ die Transaktion ebenso einfach mit svn_fs_commit_txn() hätte übergeben können. Allerdings weiß die Dateisystem-API nichts über den Hook-Mechanismus der Projektarchiv-Bibliothek. Falls Sie möchten, dass Ihr Subversion-Projektarchiv bei jeder Transaktionsübergabe eine Nicht-Subversion-Aufgabe ausführt (z.B. eine E-Mail, die alle Änderungen in dieser Transaktion beschreibt, an Ihre Entwickler-Mailingliste senden), müssen Sie diejenige Version dieser Funktion verwenden, die durch die Bibliothek libsvn_repos umschlossen ist und die Auslösung von Hooks beinhaltet – in diesem Fall svn_repos_fs_commit_txn(). (Weitere Informationen zu Subversions Projektarchiv-Hooks in „Erstellen von Projektarchiv-Hooks“.)

Lassen Sie uns nun die Sprachen wechseln. Beispiel 8.2, „Verwendung der Projektarchiv-Schicht mit Python“ ist ein Beispielprogramm, das die SWIG-Python-Bindungen von Subversion verwendet, um rekursiv die jüngste Projektarchiv-Revision zu traversieren und dabei die zahlreichen Versionspfade auszugeben.

Beispiel 8.2. Verwendung der Projektarchiv-Schicht mit Python

#!/usr/bin/python


"""Durchwandern eines Projektarchivs mit Ausgabe der Projektarchiv-Pfadnamen."""

import sys
import os.path
import svn.fs, svn.core, svn.repos

def crawl_filesystem_dir(root, directory):

    """Rekursives durchwandern von DIRECTORY unterhalb von ROOT im
    Dateisystem und eine Liste aller Pfade unterhalb von DIRECTORY
    zurückgeben."""


    # Ausgabe dieses Pfadnamens.
    print directory + "/"
    

    # Verzeichniseinträge für DIRECTORY holen.
    entries = svn.fs.svn_fs_dir_entries(root, directory)


    # Einträge abarbeiten.
    names = entries.keys()
    for name in names:

        # Den vollen Pfadnamen des Eintrags berechnen.
        full_path = directory + '/' + name


        # Falls der Eintrag ein Verzeichnis ist, rekursiv bearbeiten.
        # Die Rekursion gibt eine Liste mit dem Eintrag und all seiner
        # Kinder zurück, die der aktuellen Pfadliste hinzugefügt wird.
        if svn.fs.svn_fs_is_dir(root, full_path):
            crawl_filesystem_dir(root, full_path)
        else:

            # Sonst handelt es sich um eine Datei, also den Pfad
            # ausgeben.
            print full_path

def crawl_youngest(repos_path):

    """Öffnen des Projektarchivs bei REPOS_PATH, und rekursives
    Durchwandern seiner jüngsten Revision."""
    

    # Öffnen des Projektarchivs bei REPOS_PATH, und holen einer Referenz
    # auf sein versioniertes Dateisystem.
    repos_obj = svn.repos.svn_repos_open(repos_path)
    fs_obj = svn.repos.svn_repos_fs(repos_obj)


    # Die aktuell jüngste Revision abfragen.
    youngest_rev = svn.fs.svn_fs_youngest_rev(fs_obj)
    

    # Ein Wurzelobjekt öffnen, das die jüngste (HEAD) Revision
    # repräsentiert.
    root_obj = svn.fs.svn_fs_revision_root(fs_obj, youngest_rev)


    # Rekursiv durchwandern.
    crawl_filesystem_dir(root_obj, "")
    
if __name__ == "__main__":

    # Überprüfung auf korrekten Aufruf.
    if len(sys.argv) != 2:
        sys.stderr.write("Usage: %s REPOS_PATH\n"
                         % (os.path.basename(sys.argv[0])))
        sys.exit(1)


    # Den Projektarchiv-Pfad kanonisieren.
    repos_path = svn.core.svn_dirent_canonicalize(sys.argv[1])


    # Eigentliche Arbeit machen.
    crawl_youngest(repos_path)

Das gleiche Programm in C hätte sich noch um das APR-Speicher-Pool-System kümmern müssen. Python jedoch verwaltet den Speicher automatisch, und die Python-Bindungen von Subversion berücksichtigen diese Konvention. In C würden Sie mit angepassten Datentypen (etwa denen aus der APR-Bibliothek) arbeiten, um den Hash mit Einträgen und die Liste der Pfade zu repräsentieren, doch Python verfügt über Hashes (Dictionarys genannt) und Listen als eingebaute Datentypen und bietet eine reichhaltige Sammlung aus Funktionen, um mit diesen Datentypen umzugehen. Also übernimmt SWIG (mithilfe einiger Anpassungen in der Sprachbindungsschicht von Subversion) die Abbildung dieser speziellen Datentypen auf die spezifischen Datentypen der Zielsprache. Dies bietet Benutzern dieser Sprache eine intuitivere Schnittstelle.

Auch für Operationen in der Arbeitskopie können die Python-Bindungen von Subversion verwendet werden. Im vorangegangenen Abschnitt dieses Kapitels erwähnten wir die Schnittstelle libsvn_client und ihren Zweck zur Vereinfachung der Erstellung eines Subversion-Clients. Beispiel 8.3, „Status in Python“ ist ein kurzes Beispiel wie auf diese Bibliothek über die SWIG-Python-Bindungen zugegriffen werden kann, um eine abgespeckte Version des Befehls svn status nachzubauen.

Beispiel 8.3. Status in Python

#!/usr/bin/env python


"""Durchwandern eines Arbeitskopieverzeichnisses mit Ausgabe von Statusinformation."""

import sys
import os.path
import getopt
import svn.core, svn.client, svn.wc

def generate_status_code(status):

    """Übersetzen eines Stauswerts in einen Ein-Zeichen-Statuscode,
    wobei dieselbe Logik wie beim Subversion-Kommandozeilen-Client
    verwendet wird."""
    code_map = { svn.wc.svn_wc_status_none        : ' ',
                 svn.wc.svn_wc_status_normal      : ' ',
                 svn.wc.svn_wc_status_added       : 'A',
                 svn.wc.svn_wc_status_missing     : '!',
                 svn.wc.svn_wc_status_incomplete  : '!',
                 svn.wc.svn_wc_status_deleted     : 'D',
                 svn.wc.svn_wc_status_replaced    : 'R',
                 svn.wc.svn_wc_status_modified    : 'M',
                 svn.wc.svn_wc_status_conflicted  : 'C',
                 svn.wc.svn_wc_status_obstructed  : '~',
                 svn.wc.svn_wc_status_ignored     : 'I',
                 svn.wc.svn_wc_status_external    : 'X',
                 svn.wc.svn_wc_status_unversioned : '?',
               }
    return code_map.get(status, '?')

def do_status(wc_path, verbose, prefix):

    # Einen "Staffelstab" für den Client-Kontext erzeugen.
    ctx = svn.client.svn_client_create_context()

    def _status_callback(path, status):

        """Eine Rückruffunktion für svn_client_status."""


        # Ausgeben des Pfades, ohne den Teil, der sich mit der Wurzel
        # des zu durchlaufenden Baums überlappt
        text_status = generate_status_code(status.text_status)
        prop_status = generate_status_code(status.prop_status)
        prefix_text = ''
        if prefix is not None:
            prefix_text = prefix + " "
        print '%s%s%s  %s' % (prefix_text, text_status, prop_status, path)
        

    # Das Durchlaufen starten, _status_callback() als Rückruffunktion
    # verwenden.
    revision = svn.core.svn_opt_revision_t()
    revision.type = svn.core.svn_opt_revision_head
    svn.client.svn_client_status2(wc_path, revision, _status_callback,
                                  svn.core.svn_depth_infinity, verbose,
                                  0, 0, 1, ctx)

def usage_and_exit(errorcode):

    """Ausgabe des Verwendungshinweises und beenden mit ERRORCODE."""
    stream = errorcode and sys.stderr or sys.stdout

    stream.write("""Verwendung: %s OPTIONWN AK-PATH
Optionen:
  --help, -h    : Diesen Hinweis anzeigen
  --prefix ARG  : ARG gefolgt von einem Leerzeichen vor jeder Zeile ausgeben
  --verbose, -v : Zeige alle Status, auch uninteressante
""" % (os.path.basename(sys.argv[0])))
    sys.exit(errorcode)
    
if __name__ == '__main__':

    # Kommandozeilenoptionen parsen.
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hv",
                                   ["help", "prefix=", "verbose"])
    except getopt.GetoptError:
        usage_and_exit(1)
    verbose = 0
    prefix = None
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage_and_exit(0)
        if opt in ("--prefix"):
            prefix = arg
        if opt in ("-v", "--verbose"):
            verbose = 1
    if len(args) != 1:
        usage_and_exit(2)
            

    # Arbeitskopiepfad kanonisieren.
    wc_path = svn.core.svn_dirent_canonicalize(args[0])


    # Eigentliche Arbeit machen.
    try:
        do_status(wc_path, verbose, prefix)
    except svn.core.SubversionException, e:

        sys.stderr.write("Fehler (%d): %s\n" % (e.apr_err, e.message))
        sys.exit(1)

Wie in Beispiel 8.2, „Verwendung der Projektarchiv-Schicht mit Python“ verwendet auch dieses Programm keine Pools und benutzt meist normale Python-Datentypen.

[Warnung] Warnung

Lassen Sie Pfade, die von Anwendern mitgegeben werden, durch die passende Kanonisierungsfunktion (svn_dirent_canonicalize() oder svn_uri_canonicalize()) filtern, bevor sie an andere API-Funktionen weitergeleitet werden. Ein unterlassen kann dazu führen, dass Annahmen der darunter liegenden C-Bibliotheken nicht mehr zutreffen, was wiederum einen ziemlich plötzlichen und ungezwungenen Programmabsturz bedeutet.

Von besonderem Interesse für Anwender der Python-Variante von Subversions Programmierschnittstelle ist die Implementierung von Rückruffunktionen. Wie bereits erwähnt wurde, macht die C-Programmierschnittstelle von Subversion regen Gebrauch vom Rückruffunktion-Baton-Paradigma. Schnittstellenfunktionen, die in C ein Funktion-Baton-Paar akzeptieren, erlauben in Python nur einen Parameter mit einer Rückruffunktion. Wie soll der Aufrufer dann beliebige Kontextinformationen an die Rückruffunktion übergeben? In Python wird das durch Ausnutzung der Regeln zum Gültigkeitsbereich und der Standard-Argumentwerte erreicht. Sie können sich die Umsetzung in Beispiel 8.3, „Status in Python“ ansehen. Der Funktion svn_client_status2() wird eine Rückruffunktion (_status_callback()) aber kein Baton mitgegeben; _status_callback() hat Zugriff auf den vom Anwender zur Verfügung gestellten Präfix, da diese Variable automatisch in den Gültigkeitsbereich der Funktion fällt.



[66] Schließlich verwendet auch Subversion die APIs von Subversion.

[67] Subversion verwendet so weit wie möglich ANSI-Systemaufrufe und Datentypen.

[68] Neon und Berkeley DB sind Beispiele solcher Bibliotheken.

[69] Die Weitergabe in jeder Form muss die Information beigefügt sein, wie der vollständige Quelltext der Software beschafft werden kann, der SVNKit verwendet, sowie der begleitenden Software, die wiederum Software verwendet, die SVNKit verwendet. Zu Details, siehe http://svnkit.com/license.html.