[gtk-doc] mkdb: convert gtkdoc-mkdb to python



commit fad013949dfbbac3eb94cb93dd36c7d6cca22fde
Author: Jussi Pakkanen <jpakkane gmail com>
Date:   Sat Apr 1 23:23:45 2017 +0300

    mkdb: convert gtkdoc-mkdb to python
    
    Fixes #781157

 gtkdoc-mkdb.in      | 9420 ++++++++++++++++++++++++---------------------------
 tests/Makefile.am   |    2 +-
 tests/gtkdoc-mkdb.t |   30 -
 tests/tools.sh.in   |    2 +-
 4 files changed, 4491 insertions(+), 4963 deletions(-)
---
diff --git a/gtkdoc-mkdb.in b/gtkdoc-mkdb.in
index f065344..fd070e9 100755
--- a/gtkdoc-mkdb.in
+++ b/gtkdoc-mkdb.in
@@ -1,5 +1,5 @@
-#!@PERL@ -w
-# -*- cperl -*-
+#!@PYTHON@
+# -*- python; coding: utf-8 -*-
 #
 # gtk-doc - GTK DocBook documentation generator.
 # Copyright (C) 1998  Damon Chaplin
@@ -25,186 +25,188 @@
 # Description : This creates the DocBook files from the edited templates.
 #############################################################################
 
-use warnings;
-use strict;
-use Getopt::Long;
+from __future__ import print_function
 
-push @INC, '@PACKAGE_DATA_DIR@';
-require "gtkdoc-common.pl";
+import argparse
+import logging
+import os
+import re
+import sys
+
+from gtkdoc import common
 
 # Options
 
 # name of documentation module
-my $MODULE;
-my $TMPL_DIR;
-my $DB_OUTPUT_DIR;
-my @SOURCE_DIRS;
-my $SOURCE_SUFFIXES = "";
-my $IGNORE_FILES = "";
-my $PRINT_VERSION;
-my $PRINT_HELP;
-my $MAIN_SGML_FILE;
-my $EXPAND_CONTENT_FILES = "";
-my $INLINE_MARKUP_MODE;
-my $DEFAULT_STABILITY;
-my $DEFAULT_INCLUDES;
-my $OUTPUT_FORMAT;
-my $NAME_SPACE = "";
-my $OUTPUT_ALL_SYMBOLS;
-my $OUTPUT_SYMBOLS_WITHOUT_SINCE;
-my $ROOT_DIR = ".";
-my $OBJECT_TREE_FILE;
-my $INTERFACES_FILE;
-my $PREREQUISITES_FILE;
-my $SIGNALS_FILE;
-my $ARGS_FILE;
+MODULE = None
+TMPL_DIR = None
+DB_OUTPUT_DIR = None
+SOURCE_DIRS = None
+SOURCE_SUFFIXES = ''
+IGNORE_FILES = ''
+MAIN_SGML_FILE = None
+EXPAND_CONTENT_FILES = ''
+INLINE_MARKUP_MODE = None
+DEFAULT_STABILITY = None
+DEFAULT_INCLUDES = None
+OUTPUT_FORMAT = None
+NAME_SPACE = ''
+OUTPUT_ALL_SYMBOLS = None
+OUTPUT_SYMBOLS_WITHOUT_SINCE = None
+ROOT_DIR = "."
+OBJECT_TREE_FILE = None
+INTERFACES_FILE = None
+PREREQUISITES_FILE = None
+SIGNALS_FILE = None
+ARGS_FILE = None
 
 # These global arrays store information on signals. Each signal has an entry
 # in each of these arrays at the same index, like a multi-dimensional array.
-my @SignalObjects;        # The GtkObject which emits the signal.
-my @SignalNames;        # The signal name.
-my @SignalReturns;        # The return type.
-my @SignalFlags;        # Flags for the signal
-my @SignalPrototypes;        # The rest of the prototype of the signal handler.
+SignalObjects = []        # The GtkObject which emits the signal.
+SignalNames = []        # The signal name.
+SignalReturns = []        # The return type.
+SignalFlags = []        # Flags for the signal
+SignalPrototypes = []        # The rest of the prototype of the signal handler.
 
 # These global arrays store information on Args. Each Arg has an entry
 # in each of these arrays at the same index, like a multi-dimensional array.
-my @ArgObjects;                # The GtkObject which has the Arg.
-my @ArgNames;                # The Arg name.
-my @ArgTypes;                # The Arg type - gint, GtkArrowType etc.
-my @ArgFlags;                # How the Arg can be used - readable/writable etc.
-my @ArgNicks;                # The nickname of the Arg.
-my @ArgBlurbs;          # Docstring of the Arg.
-my @ArgDefaults;        # Default value of the Arg.
-my @ArgRanges;                # The range of the Arg type
+ArgObjects = []                # The GtkObject which has the Arg.
+ArgNames = []                # The Arg name.
+ArgTypes = []                # The Arg type - gint, GtkArrowType etc.
+ArgFlags = []                # How the Arg can be used - readable/writable etc.
+ArgNicks = []                # The nickname of the Arg.
+ArgBlurbs = []          # Docstring of the Arg.
+ArgDefaults = []        # Default value of the Arg.
+ArgRanges = []                # The range of the Arg type
+
 # These global hashes store declaration info keyed on a symbol name.
-my %Declarations;
-my %DeclarationTypes;
-my %DeclarationConditional;
-my %DeclarationOutput;
-my %Deprecated;
-my %Since;
-my %StabilityLevel;
-my %StructHasTypedef;
+Declarations = {}
+DeclarationTypes = {}
+DeclarationConditional = {}
+DeclarationOutput = {}
+Deprecated = {}
+Since = {}
+StabilityLevel = {}
+StructHasTypedef = {}
 
 # These global hashes store the existing documentation.
-my %SymbolDocs;
-my %SymbolTypes;
-my %SymbolParams;
-my %SymbolSourceFile;
-my %SymbolSourceLine;
-my %SymbolAnnotations;
+SymbolDocs = {}
+SymbolTypes = {}
+SymbolParams = {}
+SymbolSourceFile = {}
+SymbolSourceLine = {}
+SymbolAnnotations = {}
 
 # These global hashes store documentation scanned from the source files.
-my %SourceSymbolDocs;
-my %SourceSymbolParams;
-my %SourceSymbolSourceFile;
-my %SourceSymbolSourceLine;
+SourceSymbolDocs = {}
+SourceSymbolParams = {}
+SourceSymbolSourceFile = {}
+SourceSymbolSourceLine = {}
 
 # all documentation goes in here, so we can do coverage analysis
-my %AllSymbols;
-my %AllIncompleteSymbols;
-my %AllUnusedSymbols;
-my %AllDocumentedSymbols;
+AllSymbols = {}
+AllIncompleteSymbols = {}
+AllUnusedSymbols = {}
+AllDocumentedSymbols = {}
 
 # Undeclared yet documented symbols
-my %UndeclaredSymbols;
+UndeclaredSymbols = {}
 
 # These global arrays store GObject, subclasses and the hierarchy (also of
 # non-object derived types).
-my @Objects;
-my @ObjectLevels;
-my %ObjectRoots;
+Objects = []
+ObjectLevels = []
+ObjectRoots = {}
 
-my %Interfaces;
-my %Prerequisites;
+Interfaces = {}
+Prerequisites = {}
 
 # holds the symbols which are mentioned in $MODULE-sections.txt and in which
 # section they are defined
-my %KnownSymbols;
-my %SymbolSection;
-my %SymbolSectionId;
+KnownSymbols = {}
+SymbolSection = {}
+SymbolSectionId = {}
 
 # collects index entries
-my %IndexEntriesFull;
-my %IndexEntriesSince;
-my %IndexEntriesDeprecated;
+IndexEntriesFull = {}
+IndexEntriesSince = {}
+IndexEntriesDeprecated = {}
 
 # Standard C preprocessor directives, which we ignore for '#' abbreviations.
-my %PreProcessorDirectives = (
-    'assert' => 1,
-    'define' => 1,
-    'elif' => 1,
-    'else' => 1,
-    'endif' => 1,
-    'error' => 1,
-    'if' => 1,
-    'ifdef' => 1,
-    'ifndef' => 1,
-    'include' => 1,
-    'line' => 1,
-    'pragma' => 1,
-    'unassert' => 1,
-    'undef' => 1,
-    'warning' => 1
-);
+PreProcessorDirectives = {
+    'assert': 1,
+    'define': 1,
+    'elif': 1,
+    'else': 1,
+    'endif': 1,
+    'error': 1,
+    'if': 1,
+    'ifdef': 1,
+    'ifndef': 1,
+    'include': 1,
+    'line': 1,
+    'pragma': 1,
+    'unassert': 1,
+    'undef': 1,
+    'warning': 1
+}
 
 # remember used annotation (to write minimal glossary)
-my %AnnotationsUsed;
+AnnotationsUsed = {}
 
 # the regexp that parses the annotation is in ScanSourceFile()
-my %AnnotationDefinition = (
+AnnotationDefinition = {
     # the GObjectIntrospection annotations are defined at:
     # https://live.gnome.org/GObjectIntrospection/Annotations
-    'allow-none' => "NULL is OK, both for passing and for returning.",
-    'nullable' => "NULL may be passed as the value in, out, in-out; or as a return value.",
-    'not nullable' => "NULL must not be passed as the value in, out, in-out; or as a return value.",
-    'optional' => "NULL may be passed instead of a pointer to a location.",
-    'array' => "Parameter points to an array of items.",
-    'attribute' => "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
-    'attributes' => "Free-form key-value pairs.",
-    'closure' => "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
-    'constructor' => "This symbol is a constructor, not a static method.",
-    'destroy' => "This parameter is a 'destroy_data', for callbacks.",
-    'default' => "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less 
parameters).",
-    'element-type' => "Generics and defining elements of containers and arrays.",
-    'error-domains' => "Typed errors. Similar to throws in Java.",
-    'foreign' => "This is a foreign struct.",
-    'get-value-func' => "The specified function is used to convert a struct from a GValue, must be a 
GTypeInstance.",
-    'in' => "Parameter for input. Default is <acronym>transfer none</acronym>.",
-    'inout' => "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
-    'in-out' => "Parameter for input and for returning results. Default is <acronym>transfer 
full</acronym>.",
-    'method' => "This is a method",
-    'not-error' => "A GError parameter is not to be handled like a normal GError.",
-    'out' => "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
-    'out caller-allocates' => "Out parameter, where caller must allocate storage.",
-    'out callee-allocates' => "Out parameter, where caller must allocate storage.",
-    'ref-func' => "The specified function is used to ref a struct, must be a GTypeInstance.",
-    'rename-to' => "Rename the original symbol's name to SYMBOL.",
-    'scope call' => "The callback is valid only during the call to the method.",
-    'scope async' => "The callback is valid until first called.",
-    'scope notified' => "The callback is valid until the GDestroyNotify argument is called.",
-    'set-value-func' => "The specified function is used to convert from a struct to a GValue, must be a 
GTypeInstance.",
-    'skip' => "Exposed in C code, not necessarily available in other languages.",
-    'transfer container' => "Free data container after the code is done.",
-    'transfer floating' => "Alias for <acronym>transfer none</acronym>, used for objects with floating 
refs.",
-    'transfer full' => "Free data after the code is done.",
-    'transfer none' => "Don't free data after the code is done.",
-    'type' => "Override the parsed C type with given type.",
-    'unref-func' => "The specified function is used to unref a struct, must be a GTypeInstance.",
-    'virtual' => "This is the invoker for a virtual method.",
-    'value' => "The specified value overrides the evaluated value of the constant.",
+    'allow-none': "NULL is OK, both for passing and for returning.",
+    'nullable': "NULL may be passed as the value in, out, in-out; or as a return value.",
+    'not nullable': "NULL must not be passed as the value in, out, in-out; or as a return value.",
+    'optional': "NULL may be passed instead of a pointer to a location.",
+    'array': "Parameter points to an array of items.",
+    'attribute': "Deprecated free-form custom annotation, replaced by (attributes) annotation.",
+    'attributes': "Free-form key-value pairs.",
+    'closure': "This parameter is a 'user_data', for callbacks; many bindings can pass NULL here.",
+    'constructor': "This symbol is a constructor, not a static method.",
+    'destroy': "This parameter is a 'destroy_data', for callbacks.",
+    'default': "Default parameter value (for in case the <acronym>shadows</acronym>-to function has less 
parameters).",
+    'element-type': "Generics and defining elements of containers and arrays.",
+    'error-domains': "Typed errors. Similar to throws in Java.",
+    'foreign': "This is a foreign struct.",
+    'get-value-func': "The specified function is used to convert a struct from a GValue, must be a 
GTypeInstance.",
+    'in': "Parameter for input. Default is <acronym>transfer none</acronym>.",
+    'inout': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
+    'in-out': "Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.",
+    'method': "This is a method",
+    'not-error': "A GError parameter is not to be handled like a normal GError.",
+    'out': "Parameter for returning results. Default is <acronym>transfer full</acronym>.",
+    'out caller-allocates': "Out parameter, where caller must allocate storage.",
+    'out callee-allocates': "Out parameter, where caller must allocate storage.",
+    'ref-func': "The specified function is used to ref a struct, must be a GTypeInstance.",
+    'rename-to': "Rename the original symbol's name to SYMBOL.",
+    'scope call': "The callback is valid only during the call to the method.",
+    'scope async': "The callback is valid until first called.",
+    'scope notified': "The callback is valid until the GDestroyNotify argument is called.",
+    'set-value-func': "The specified function is used to convert from a struct to a GValue, must be a 
GTypeInstance.",
+    'skip': "Exposed in C code, not necessarily available in other languages.",
+    'transfer container': "Free data container after the code is done.",
+    'transfer floating': "Alias for <acronym>transfer none</acronym>, used for objects with floating refs.",
+    'transfer full': "Free data after the code is done.",
+    'transfer none': "Don't free data after the code is done.",
+    'type': "Override the parsed C type with given type.",
+    'unref-func': "The specified function is used to unref a struct, must be a GTypeInstance.",
+    'virtual': "This is the invoker for a virtual method.",
+    'value': "The specified value overrides the evaluated value of the constant.",
     # Stability Level definition
     # https://bugzilla.gnome.org/show_bug.cgi?id=170860
-    'Stable' => <<EOF,
+    'Stable': '''
 The intention of a Stable interface is to enable arbitrary third parties to
 develop applications to these interfaces, release them, and have confidence that
 they will run on all minor releases of the product (after the one in which the
 interface was introduced, and within the same major release). Even at a major
 release, incompatible changes are expected to be rare, and to have strong
 justifications.
-EOF
-    'Unstable' => <<EOF,
+    ''',
+    'Unstable': '''
 Unstable interfaces are experimental or transitional. They are typically used to
 give outside developers early access to new or rapidly changing technology, or
 to provide an interim solution to a problem where a more general solution is
@@ -218,171 +220,143 @@ Given such caveats, customer impact need not be a factor when considering
 incompatible changes to an Unstable interface in a major or minor release.
 Nonetheless, when such changes are introduced, the changes should still be
 mentioned in the release notes for the affected release.
-EOF
-    'Private' => <<EOF
+    ''',
+    'Private': '''
 An interface that can be used within the GNOME stack itself, but that is not
 documented for end-users.  Such functions should only be used in specified and
 documented ways.
-EOF
-);
+    ''',
+}
 
 # Elements to consider non-block items in MarkDown parsing
-my %MD_TEXT_LEVEL_ELEMENTS = ( "literal" => 1,
-                               "emphasis" => 1,
-                               "envar" => 1,
-                               "filename" => 1,
-                               "firstterm" => 1,
-                               "footnote" => 1,
-                               "function" => 1,
-                               "manvolnum" => 1,
-                               "option" => 1,
-                               "replaceable" => 1,
-                               "structfield" => 1,
-                               "structname" => 1,
-                               "title" => 1,
-                               "varname" => 1 );
-my %MD_ESCAPABLE_CHARS = ( "\\" => 1,
-                           "`" => 1,
-                           "*" => 1,
-                           "_" => 1,
-                           "{" => 1,
-                           "}" => 1,
-                           "[" => 1,
-                           "]" => 1,
-                           "(" => 1,
-                           ")" => 1,
-                           ">" => 1,
-                           "#" => 1,
-                           "+" => 1,
-                           "-" => 1,
-                           "." => 1,
-                           "!" => 1 );
-my %MD_GTK_ESCAPABLE_CHARS = ( "@" => 1,
-                               "%" => 1 );
+MD_TEXT_LEVEL_ELEMENTS = {"literal": 1,
+                          "emphasis": 1,
+                          "envar": 1,
+                          "filename": 1,
+                          "firstterm": 1,
+                          "footnote": 1,
+                          "function": 1,
+                          "manvolnum": 1,
+                          "option": 1,
+                          "replaceable": 1,
+                          "structfield": 1,
+                          "structname": 1,
+                          "title": 1,
+                          "varname": 1,
+                         }
+MD_ESCAPABLE_CHARS = {"\\": 1,
+                      "`": 1,
+                      "*": 1,
+                      "_": 1,
+                      "{": 1,
+                      "}": 1,
+                      "[": 1,
+                      "]": 1,
+                      "(": 1,
+                      ")": 1,
+                      ">": 1,
+                      "#": 1,
+                      "+": 1,
+                      "-": 1,
+                      ".": 1,
+                      "!": 1,
+                     }
+MD_GTK_ESCAPABLE_CHARS = {"@": 1,
+                          "%": 1,
+                         }
 
 # Function and other declaration output settings.
-my $RETURN_TYPE_FIELD_WIDTH = 20;
-my $SYMBOL_FIELD_WIDTH = 36;
-my $MAX_SYMBOL_FIELD_WIDTH = 40;
-my $SIGNAL_FIELD_WIDTH = 16;
-my $PARAM_FIELD_COUNT = 2;
+RETURN_TYPE_FIELD_WIDTH = 20
+SYMBOL_FIELD_WIDTH = 36
+MAX_SYMBOL_FIELD_WIDTH = 40
+SIGNAL_FIELD_WIDTH = 16
+PARAM_FIELD_COUNT = 2
 
 # XML, SGML formatting helper
-my $doctype_header;
-
-
-Run() unless caller; # Run program unless loaded as a module
-
-
-sub Run {
-    my %optctl = ('module' => \$MODULE,
-                  'source-dir' => \@SOURCE_DIRS,
-                  'source-suffixes' => \$SOURCE_SUFFIXES,
-                  'ignore-files' => \$IGNORE_FILES,
-                  'output-dir' => \$DB_OUTPUT_DIR,
-                  'tmpl-dir' => \$TMPL_DIR,
-                  'version' => \$PRINT_VERSION,
-                  'help' => \$PRINT_HELP,
-                  'main-sgml-file' => \$MAIN_SGML_FILE,
-                  'expand-content-files' => \$EXPAND_CONTENT_FILES,
-                  'sgml-mode' => \$INLINE_MARKUP_MODE,
-                  'xml-mode' => \$INLINE_MARKUP_MODE,
-                  'default-stability' => \$DEFAULT_STABILITY,
-                  'default-includes' => \$DEFAULT_INCLUDES,
-                  'output-format' => \$OUTPUT_FORMAT,
-                  'name-space' => \$NAME_SPACE,
-                  'outputallsymbols' => \$OUTPUT_ALL_SYMBOLS,
-                  'outputsymbolswithoutsince' => \$OUTPUT_SYMBOLS_WITHOUT_SINCE
-                  );
-    GetOptions(\%optctl, "module=s", "source-dir:s", "source-suffixes:s",
-        "ignore-files:s", "output-dir:s", "tmpl-dir:s", "version",
-        "outputallsymbols", "outputsymbolswithoutsince",
-        "expand-content-files:s", "main-sgml-file:s", "extra-db-files:s", "help",
-        "sgml-mode", "xml-mode", "default-stability:s", "default-includes:s",
-        "output-format:s", "name-space:s");
-
-    if ($PRINT_VERSION) {
-        print "@VERSION@\n";
-        exit 0;
-    }
-
-    if (!$MODULE) {
-        $PRINT_HELP = 1;
-    }
-
-    if ($DEFAULT_STABILITY && $DEFAULT_STABILITY ne "Stable"
-        && $DEFAULT_STABILITY ne "Private" && $DEFAULT_STABILITY ne "Unstable") {
-        $PRINT_HELP = 1;
-    }
-
-    if ($PRINT_HELP) {
-        print <<EOF;
-gtkdoc-mkdb version @VERSION@ - generate docbook files
-
---module=MODULE_NAME       Name of the doc module being parsed
---source-dir=DIRNAME       Directories which contain inline reference material
---source-suffixes=SUFFIXES Suffixes of source files to scan, comma-separated
---ignore-files=FILES       A space-separated list of header files/dirs not to
-                           scan
---output-dir=DIRNAME       Directory to put the generated DocBook files in
---tmpl-dir=DIRNAME         Directory in which template files may be found
---main-sgml-file=FILE      File containing the toplevel DocBook file.
---expand-content-files=FILES Extra DocBook files to expand abbreviations in.
---output-format=FORMAT     Format to use for the generated docbook, XML or SGML.
---{xml,sgml}-mode          Allow DocBook markup in inline documentation.
---default-stability=LEVEL  Specify default stability Level. Valid values are
-                           Stable, Unstable, or Private.
---default-includes=FILENAMES Specify default includes for section Synopsis
---name-space=NS            Omit namespace in index.
---version                  Print the version of this program
---help                     Print this help
-EOF
-        exit 0;
-    }
-
-    @TRACE@(" ignore files: [$IGNORE_FILES]\n");
+doctype_header = None
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--module', default='')
+parser.add_argument('--source-dir', dest='source_dir', default='')
+parser.add_argument('--source-suffixes', dest='source_suffixes', default='')
+parser.add_argument('--ignore-files', dest='ignore_files', default='')
+parser.add_argument('--output-dir', dest='output_dir', default='')
+parser.add_argument('--tmpl-dir', dest='tmpl_dir', default='')
+parser.add_argument('--main-sgml-file', dest='main_sgml_file', default='')
+parser.add_argument('--expand-content-files', dest='expand_content_files', default='')
+group = parser.add_mutually_exclusive_group()
+group.add_argument('--sgml-mode', action='store_true', default=False, dest='sgml_mode')
+group.add_argument('--xml-mode', action='store_true', default=False, dest='xml_mode')
+parser.add_argument('--default-stability', dest='default_stability', choices=['Stable', 'Private', 
'Unstable'], default='Stable')
+parser.add_argument('--default-includes', dest='default_includes', default='')
+parser.add_argument('--output-format', dest='default_format', default='')
+parser.add_argument('--name-space', dest='name_space', default='')
+parser.add_argument('--outputallsymbols', default=False, action='store_true')
+parser.add_argument('--outputsymbolswithoutsince', default=False, action='store_true')
+
+
+def Run():
+    global MODULE, TMPL_DIR, SOURCE_DIRS, SOURCE_SUFFIXES, IGNORE_FILES, MAIN_SGML_FILE, 
EXPAND_CONTENT_FILES, \
+        INLINE_MARKUP_MODE, DEFAULT_STABILITY, DEFAULT_INCLUDES, OUTPUT_FORMAT, NAME_SPACE, 
OUTPUT_ALL_SYMBOLS, \
+        OUTPUT_SYMBOLS_WITHOUT_SINCE, DB_OUTPUT_DIR, OBJECT_TREE_FILE, INTERFACES_FILE, PREREQUISITES_FILE, \
+        SIGNALS_FILE, ARGS_FILE, doctype_header
+
+    options = parser.parse_args()
+
+    # We should pass the options variable around instead of this global variable horror
+    # but too much of the code expects these to be around. Fix this once the transition is done.
+    MODULE = options.module
+    TMPL_DIR = options.tmpl_dir
+    SOURCE_DIRS = options.source_dir
+    SOURCE_SUFFIXES = options.source_suffixes
+    IGNORE_FILES = options.ignore_files
+    MAIN_SGML_FILE = options.main_sgml_file
+    EXPAND_CONTENT_FILES = options.expand_content_files
+    INLINE_MARKUP_MODE = options.xml_mode or options.sgml_mode
+    DEFAULT_STABILITY = options.default_stability
+    DEFAULT_INCLUDES = options.default_includes
+    OUTPUT_FORMAT = options.default_format
+    NAME_SPACE = options.name_space
+    OUTPUT_ALL_SYMBOLS = options.outputallsymbols
+    OUTPUT_SYMBOLS_WITHOUT_SINCE = options.outputsymbolswithoutsince
+
+    logging.info(" ignore files: " + IGNORE_FILES)
 
     # check output format
-    if (! defined($OUTPUT_FORMAT) || ($OUTPUT_FORMAT eq "")) {
-        $OUTPUT_FORMAT = "xml";
-    } else {
-        $OUTPUT_FORMAT = lc($OUTPUT_FORMAT);
-    }
-    if ($OUTPUT_FORMAT ne "xml") {
-        die "Invalid format '$OUTPUT_FORMAT' passed to --output.format"
-    }
-
-    if (!$MAIN_SGML_FILE) {
+    if OUTPUT_FORMAT is None or OUTPUT_FORMAT == '':
+        OUTPUT_FORMAT = "xml"
+    else:
+        OUTPUT_FORMAT = OUTPUT_FORMAT.lower()
+    if OUTPUT_FORMAT != "xml":
+        sys.exit("Invalid format '%s' passed to --output.format" % OUTPUT_FORMAT)
+
+    if not MAIN_SGML_FILE:
         # backwards compatibility
-        if (-e "${MODULE}-docs.sgml") {
-            $MAIN_SGML_FILE = "${MODULE}-docs.sgml";
-        } else {
-            $MAIN_SGML_FILE = "${MODULE}-docs.xml";
-        }
-    }
+        if os.path.exists(MODULE + "-docs.sgml"):
+            MAIN_SGML_FILE = MODULE + "-docs.sgml"
+        else:
+            MAIN_SGML_FILE = MODULE + "-docs.xml"
 
     # extract docbook header or define default
-    if (-e $MAIN_SGML_FILE) {
-        open(INPUT, "<$MAIN_SGML_FILE") || die "Can't open $MAIN_SGML_FILE";
-        $doctype_header = "";
-        while (<INPUT>) {
-            if (/^\s*<(book|chapter|article)/) {
+    if os.path.exists(MAIN_SGML_FILE):
+        INPUT = open(MAIN_SGML_FILE)
+        doctype_header = ''
+        for line in INPUT:
+            if re.search(r'^\s*<(book|chapter|article)', line):
                 # check that the top-level tagSYSTEM or the doctype decl contain the xinclude namespace decl
-                if (($_ !~ m/http:\/\/www.w3.org\/200[13]\/XInclude/) && ($doctype_header !~ 
m/http:\/\/www.w3.org\/200[13]\/XInclude/m)) {
-                    $doctype_header = "";
-                }
-                last;
-            }
+                if not re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', line) and not 
re.search(r'http:\/\/www.w3.org\/200[13]\/XInclude', doctype_header, flags=re.MULTILINE):
+                    doctype_header = ''
+                break
+
             # if there are SYSTEM ENTITIES here, we should prepend "../" to the path
             # FIXME: not sure if we can do this now, as people already work-around the problem
             # s#<!ENTITY % ([a-zA-Z-]+) SYSTEM \"([^/][a-zA-Z./]+)\">#<!ENTITY % $1 SYSTEM \"../$2\">#;
-            s#<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
-            $doctype_header .= $_;
-        }
-        close(INPUT);
-    } else {
-        $doctype_header = <<EOF;
-<?xml version="1.0"?>
+            #<!ENTITY % gtkdocentities SYSTEM \"([^"]*)\">#<!ENTITY % gtkdocentities SYSTEM \"../$1\">#;
+            doctype_header += line
+        INPUT.close()
+    else:
+        doctype_header = '''<?xml version="1.0"?>
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
                "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd";
 [
@@ -390,122 +364,112 @@ EOF
   <!ENTITY % gtkdocentities SYSTEM "../xml/gtkdocentities.ent">
   %gtkdocentities;
 ]>
-EOF
-    }
-    chomp($doctype_header);
+'''
+
+    doctype_header = doctype_header.strip()
 
     # All the files are written in subdirectories beneath here.
-    $TMPL_DIR = $TMPL_DIR ? $TMPL_DIR : "$ROOT_DIR/tmpl";
+    TMPL_DIR = TMPL_DIR if TMPL_DIR else os.path.join(ROOT_DIR, "tmpl")
 
     # This is where we put all the DocBook output.
-    $DB_OUTPUT_DIR = $DB_OUTPUT_DIR ? $DB_OUTPUT_DIR : "$ROOT_DIR/xml";
+    DB_OUTPUT_DIR = DB_OUTPUT_DIR if DB_OUTPUT_DIR else os.path.join(ROOT_DIR, "xml")
 
     # This file contains the object hierarchy.
-    $OBJECT_TREE_FILE = "$ROOT_DIR/$MODULE.hierarchy";
+    OBJECT_TREE_FILE = os.path.join(ROOT_DIR, MODULE + ".hierarchy")
 
     # This file contains the interfaces.
-    $INTERFACES_FILE = "$ROOT_DIR/$MODULE.interfaces";
+    INTERFACES_FILE = os.path.join(ROOT_DIR, MODULE + ".interfaces")
 
     # This file contains the prerequisites.
-    $PREREQUISITES_FILE = "$ROOT_DIR/$MODULE.prerequisites";
+    PREREQUISITES_FILE = os.path.join(ROOT_DIR, MODULE + ".prerequisites")
 
     # This file contains signal arguments and names.
-    $SIGNALS_FILE = "$ROOT_DIR/$MODULE.signals";
+    SIGNALS_FILE = os.path.join(ROOT_DIR, MODULE + ".signals")
 
     # The file containing Arg information.
-    $ARGS_FILE = "$ROOT_DIR/$MODULE.args";
+    ARGS_FILE = os.path.join(ROOT_DIR, MODULE + ".args")
 
     # Create the root DocBook output directory if it doens't exist.
-    if (! -e $DB_OUTPUT_DIR) {
-        mkdir ("$DB_OUTPUT_DIR", 0777)
-            || die "Can't create directory: $DB_OUTPUT_DIR";
-    }
+    if not os.path.isdir(DB_OUTPUT_DIR):
+        os.mkdir(DB_OUTPUT_DIR)
 
-    &ReadKnownSymbols ("$ROOT_DIR/$MODULE-sections.txt");
-    &ReadSignalsFile ($SIGNALS_FILE);
-    &ReadArgsFile ($ARGS_FILE);
-    &ReadObjectHierarchy;
-    &ReadInterfaces;
-    &ReadPrerequisites;
+    ReadKnownSymbols(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
+    ReadSignalsFile(SIGNALS_FILE)
+    ReadArgsFile(ARGS_FILE)
+    ReadObjectHierarchy()
+    ReadInterfaces()
+    ReadPrerequisites()
 
-    &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-decl.txt", 0);
-    if (-f "$ROOT_DIR/$MODULE-overrides.txt") {
-        &ReadDeclarationsFile ("$ROOT_DIR/$MODULE-overrides.txt", 1);
-    }
+    ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-decl.txt"), 0)
+    if os.path.isfile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt")):
+        ReadDeclarationsFile(os.path.join(ROOT_DIR, MODULE + "-overrides.txt"), 1)
 
-    for my $dir (@SOURCE_DIRS) {
-        &ReadSourceDocumentation ($dir);
-    }
+    for sdir in SOURCE_DIRS:
+        ReadSourceDocumentation(sdir)
 
-    my $changed = &OutputDB ("$ROOT_DIR/$MODULE-sections.txt");
+    changed = OutputDB(os.path.join(ROOT_DIR, MODULE + "-sections.txt"))
 
     # If any of the DocBook files have changed, update the timestamp file (so
     # it can be used for Makefile dependencies).
-    if ($changed || ! -e "$ROOT_DIR/sgml.stamp") {
+    if changed or not os.path.exists(os.path.join(ROOT_DIR, "sgml.stamp")):
 
         # try to detect the common prefix
         # GtkWidget, GTK_WIDGET, gtk_widget -> gtk
-        if ($NAME_SPACE eq "") {
-            $NAME_SPACE="";
-            my $pos=0;
-            my $ratio=0.0;
-            do {
-                my %prefix;
-                my $letter="";
-                foreach my $symbol (keys(%IndexEntriesFull)) {
-                    if(($NAME_SPACE eq "") || $symbol =~ /^$NAME_SPACE/i) {
-                        if (length($symbol)>$pos) {
-                            $letter=substr($symbol,$pos,1);
+        if NAME_SPACE == '':
+            NAME_SPACE = ''
+            pos = 0
+            ratio = 0.0
+            while True:
+                prefix = {}
+                letter = ''
+                for symbol in IndexEntriesFull.keys():
+                    if NAME_SPACE == '' or NAME_SPACE.lower() in symbol.lower():
+                        if len(symbol) > pos:
+                            letter = symbol[pos:pos+1]
                             # stop prefix scanning
-                            if ($letter eq "_") {
+                            if letter == "_":
                                 # stop on "_"
-                                last;
-                            }
+                                break
                             # Should we also stop on a uppercase char, if last was lowercase
                             #   GtkWidget, if we have the 'W' and had the 't' before
                             # or should we count upper and lowercase, and stop one 2nd uppercase, if we 
already had a lowercase
                             #   GtkWidget, the 'W' would be the 2nd uppercase and with 't','k' we had 
lowercase chars before
                             # need to recound each time as this is per symbol
-                            $prefix{uc($letter)}++;
-                        }
-                    }
-                }
-                if ($letter ne "" && $letter ne "_") {
-                    my $maxletter="";
-                    my $maxsymbols=0;
-                    foreach $letter (keys(%prefix)) {
+                            ul = letter.upper()
+                            if ul in prefix:
+                                prefix[ul] += 1
+                            else:
+                                prefix[ul] = 1
+
+                if letter != '' and letter != "_":
+                    maxletter = ''
+                    maxsymbols = 0
+                    for letter in prefix.keys():
                         #print "$letter: $prefix{$letter}.\n";
-                        if ($prefix{$letter}>$maxsymbols) {
-                            $maxletter=$letter;
-                            $maxsymbols=$prefix{$letter};
-                        }
-                    }
-                    $ratio = scalar(keys(%IndexEntriesFull)) / $prefix{$maxletter};
+                        if prefix[letter] > maxsymbols:
+                            maxletter = letter
+                            maxsymbols = prefix[letter]
+
+                    ratio = float(len(IndexEntriesFull)) / prefix[maxletter]
                     #print "most symbols start with $maxletter, that is ". (100 * $ratio) ." %\n";
-                    if ($ratio > 0.9) {
+                    if ratio > 0.9:
                         # do another round
-                        $NAME_SPACE .= $maxletter;
-                    }
-                    $pos++;
-                }
-                else {
-                    $ratio=0.0;
-                }
-            } while ($ratio > 0.9);
-            #print "most symbols start with $NAME_SPACE\n";
-        }
-
-        &OutputIndexFull;
-        &OutputDeprecatedIndex;
-        &OutputSinceIndexes;
-        &OutputAnnotationGlossary;
-
-        open (TIMESTAMP, ">$ROOT_DIR/sgml.stamp")
-            || die "Can't create $ROOT_DIR/sgml.stamp: $!";
-        print (TIMESTAMP "timestamp");
-        close (TIMESTAMP);
-    }
-}
+                        NAME_SPACE += maxletter
+
+                    pos += 1
+
+                else:
+                    ratio = 0.0
+
+                if ratio > 0.9:
+                    break
+
+        OutputIndexFull()
+        OutputDeprecatedIndex()
+        OutputSinceIndexes()
+        OutputAnnotationGlossary()
+
+        open(os.path.join(ROOT_DIR, 'sgml.stamp'), 'w').write('timestamp')
 
 #############################################################################
 # Function    : OutputObjectList
@@ -516,53 +480,50 @@ EOF
 # Arguments   : none
 #############################################################################
 
-sub OutputObjectList {
-    my $cols = 3;
+def OutputObjectList():
+    cols = 3
 
     # FIXME: use .xml
     # my $old_object_index = "$DB_OUTPUT_DIR/object_index.xml";
-    my $old_object_index = "$DB_OUTPUT_DIR/object_index.sgml";
-    my $new_object_index = "$DB_OUTPUT_DIR/object_index.new";
+    old_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.sgml")
+    new_object_index = os.path.join(DB_OUTPUT_DIR, "object_index.new")
 
-    open (OUTPUT, ">$new_object_index")
-        || die "Can't create $new_object_index: $!";
+    OUTPUT = open(new_object_index, 'w')
 
-    print (OUTPUT <<EOF);
-${\( MakeDocHeader ("informaltable") )}
+    OUTPUT.write('''
+%s
 <informaltable pgwide="1" frame="none">
 <tgroup cols="$cols">
 <colspec colwidth="1*"/>
 <colspec colwidth="1*"/>
 <colspec colwidth="1*"/>
 <tbody>
-EOF
-
-    my $count = 0;
-    my $object;
-    foreach $object (sort (@Objects)) {
-        my $xref = &MakeXRef ($object);
-        if ($count % $cols == 0) { print (OUTPUT "<row>\n"); }
-        print (OUTPUT "<entry>$xref</entry>\n");
-        if ($count % $cols == ($cols - 1)) { print (OUTPUT "</row>\n"); }
-        $count++;
-    }
-    if ($count == 0) {
+''' % MakeDocHeader("informaltable"))
+
+    count = 0
+    object = None
+    for object in sorted(Objects):
+        xref = MakeXRef(object)
+        if count % cols == 0:
+            OUTPUT.write("<row>\n")
+        OUTPUT.write("<entry>%s</entry>\n" % xref)
+        if count % cols == cols - 1:
+            OUTPUT.write("</row>\n")
+        count += 1
+
+    if count == 0:
         # emit an empty row, since empty tables are invalid
-        print (OUTPUT "<row><entry> </entry></row>\n");
-    }
-    else {
-        if ($count % $cols > 0) {
-            print (OUTPUT "</row>\n");
-        }
-    }
-
-    print (OUTPUT <<EOF);
-</tbody></tgroup></informaltable>
-EOF
-    close (OUTPUT);
-
-    &UpdateFileIfChanged ($old_object_index, $new_object_index, 0);
-}
+        OUTPUT.write("<row><entry> </entry></row>\n")
+
+    else:
+        if count % cols > 0:
+            OUTPUT.write("</row>\n")
+
+    OUTPUT.write('''</tbody></tgroup></informaltable>\n''')
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_object_index, new_object_index, 0)
+
 
 #############################################################################
 # Function    : TrimTextBlock
@@ -571,16 +532,13 @@ EOF
 # Arguments   : $desc - the text block to trim. May contain newlines.
 #############################################################################
 
-sub TrimTextBlock {
-  my ($desc) = @_;
+def TrimTextBlock(desc):
+    # strip leading spaces on the block
+    desc = re.sub(r'^\s+', '', desc)
+    # strip trailing spaces on every line
+    desc = re.sub(r'\s+$', '\n', desc, flags=re.MULTILINE)
 
-  # strip leading spaces on the block
-  $desc =~ s/^\s+//s;
-  # strip trailing spaces on every line
-  $desc =~ s/\s+$/\n/mg;
-
-  return $desc;
-}
+    return desc
 
 
 #############################################################################
@@ -592,125 +550,125 @@ sub TrimTextBlock {
 #                into sections and subsections.
 #############################################################################
 
-sub OutputDB {
-    my ($file) = @_;
-
-    @TRACE@("Reading: $file\n");
-    open (INPUT, $file)
-        || die "Can't open $file: $!";
-    my $filename = "";
-    my $book_top = "";
-    my $book_bottom = "";
-    my $includes = (defined $DEFAULT_INCLUDES) ? $DEFAULT_INCLUDES : "";
-    my $section_includes = "";
-    my $in_section = 0;
-    my $title = "";
-    my $section_id = "";
-    my $subsection = "";
-    my $num_symbols;
-    my $changed = 0;
-    my $functions_synop = "";
-    my $other_synop = "";
-    my $functions_details = "";
-    my $other_details = "";
-    my $signals_synop = "";
-    my $signals_desc = "";
-    my $args_synop = "";
-    my $child_args_synop = "";
-    my $style_args_synop = "";
-    my $args_desc = "";
-    my $child_args_desc = "";
-    my $style_args_desc = "";
-    my $hierarchy_str = "";
-    my @hierarchy = ();
-    my $interfaces = "";
-    my $implementations = "";
-    my $prerequisites = "";
-    my $derived = "";
-    my @file_objects = ();
-    my %templates = ();
-    my %symbol_def_line = ();
+def OutputDB(file):
+
+    logging.info("Reading: %s", file)
+    INPUT = open(file)
+    filename = ''
+    book_top = ''
+    book_bottom = ''
+    includes = DEFAULT_INCLUDES if DEFAULT_INCLUDES else ''
+    section_includes = ''
+    in_section = 0
+    title = ''
+    section_id = ''
+    subsection = ''
+    num_symbols = 0
+    changed = 0
+    functions_synop = ''
+    other_synop = ''
+    functions_details = ''
+    other_details = ''
+    signals_synop = ''
+    signals_desc = ''
+    args_synop = ''
+    child_args_synop = ''
+    style_args_synop = ''
+    args_desc = ''
+    child_args_desc = ''
+    style_args_desc = ''
+    hierarchy_str = ''
+    hierarchy = []
+    interfaces = ''
+    implementations = ''
+    prerequisites = ''
+    derived = ''
+    file_objects = []
+    templates = {}
+    symbol_def_line = {}
 
     # merge the source docs, in case there are no templates
-    &MergeSourceDocumentation;
+    MergeSourceDocumentation()
+
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+
+        if line.strip() == '':
+            continue
+
+        m1 = re.search(r'^<SUBSECTION\s*(.*)', line)
+        m2 = re.search(r'^<TITLE>(.*)<\/TITLE', line)
+        m3 = re.search(r'^<FILE>(.*)<\/FILE>', line)
+        m4 = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
+        m5 = re.search(r'^(\S+)', line)
+
+        if line.startswith('<SECTION>'):
+            num_symbols = 0
+            in_section = False
+            file_objects = []
+            symbol_def_line = {}
+
+        elif m1:
+            other_synop += "\n"
+            functions_synop += "\n"
+            subsection = m1.group(1)
+
+        elif re.search(r'^<SUBSECTION>', line):
+            pass
+        elif m2:
+            title = m2.group(1)
+            logging.info("Section: %s", title)
 
-    while (<INPUT>) {
-        if (m/^#/) {
-            next;
+            # We don't want warnings if object & class structs aren't used.
+            DeclarationOutput[title] = 1
+            DeclarationOutput["%sClass" % title] = 1
+            DeclarationOutput["%sIface" % title] = 1
+            DeclarationOutput["%sInterface" % title] = 1
+
+        elif m3:
+            filename = m3.group(1)
+            if filename in templates:
+                if ReadTemplateFile(os.path.join(TMPL_DIR, filename), 1):
+                    MergeSourceDocumentation()
+                    templates[filename] = line_number
+            else:
+                common.LogWarning(file, line_number, ("Double <FILE>%s</FILE> entry. " % file) + "Previous 
occurrence on line " + templates[filename] + ".")
+            if title == ''  and ("%s/%s:Title" % (TMPL_DIR, filename)) & SourceSymbolDocs:
+                title = SourceSymbolDocs["%s/%s:Title" % (TMPL_DIR, filename)]
+                 # Remove trailing blanks
+                title = title.rstrip()
+
+        elif m4:
+            if in_section:
+                section_includes = m4.group(1)
+            else:
+                if DEFAULT_INCLUDES:
+                    common.LogWarning(file, line_number, "Default <INCLUDE> being overridden by command line 
option.")
+                else:
+                    includes = m4.group(1)
+
+        elif re.search(r'^<\/SECTION>', line):
+            logging.info("End of section: $title\n")
+            if num_symbols > 0:
+                # collect documents
+                book_bottom += "    <xi:include href=\"xml/%s.xml\"/>\n" % filename
 
-        } elsif (m/^<SECTION>/) {
-            $num_symbols = 0;
-            $in_section = 1;
-            @file_objects = ();
-            %symbol_def_line = ();
+                i = os.path.join(TMPL_DIR, filename + ":Include")
 
-        } elsif (m/^<SUBSECTION\s*(.*)>/i) {
-            $other_synop .= "\n";
-            $functions_synop .= "\n";
-            $subsection = $1;
+                if i in SourceSymbolDocs:
+                    if section_includes:
+                        common.LogWarning(file, line_number, "Section <INCLUDE> being overridden by inline 
comments.")
+                    section_includes = SourceSymbolDocs[i]
 
-        } elsif (m/^<SUBSECTION>/) {
+                if section_includes == '':
+                    section_includes = includes
 
-        } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
-            $title = $1;
-            @TRACE@("Section: $title\n");
+                signals_synop = re.sub(r'^\n*', '', signals_synop)
+                signals_synop = re.sub(r'\n+$', '\n', signals_synop)
 
-            # We don't want warnings if object & class structs aren't used.
-            $DeclarationOutput{$title} = 1;
-            $DeclarationOutput{"${title}Class"} = 1;
-            $DeclarationOutput{"${title}Iface"} = 1;
-            $DeclarationOutput{"${title}Interface"} = 1;
-
-        } elsif (m/^<FILE>(.*)<\/FILE>/) {
-            $filename = $1;
-            if (! defined $templates{$filename}) {
-               if (&ReadTemplateFile ("$TMPL_DIR/$filename", 1)) {
-                   &MergeSourceDocumentation;
-                   $templates{$filename}=$.;
-               }
-            } else {
-                &LogWarning ($file, $., "Double <FILE>$filename</FILE> entry. ".
-                    "Previous occurrence on line ".$templates{$filename}.".");
-            }
-            if (($title eq "") and (defined $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"})) {
-                $title = $SourceSymbolDocs{"$TMPL_DIR/$filename:Title"};
-                 # Remove trailing blanks
-                $title =~ s/\s+$//;
-           }
-
-        } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
-            if ($in_section) {
-                $section_includes = $1;
-            } else {
-                if (defined $DEFAULT_INCLUDES) {
-                    &LogWarning ($file, $., "Default <INCLUDE> being overridden by command line option.");
-                }
-                else {
-                    $includes = $1;
-                }
-            }
-
-        } elsif (m/^<\/SECTION>/) {
-            @TRACE@("End of section: $title\n");
-            if ($num_symbols > 0) {
-                # collect documents
-                $book_bottom .= "    <xi:include href=\"xml/$filename.xml\"/>\n";
-
-                if (defined ($SourceSymbolDocs{"$TMPL_DIR/$filename:Include"})) {
-                    if ($section_includes) {
-                        &LogWarning ($file, $., "Section <INCLUDE> being overridden by inline comments.");
-                    }
-                    $section_includes = $SourceSymbolDocs{"$TMPL_DIR/$filename:Include"};
-                }
-                if ($section_includes eq "") {
-                    $section_includes = $includes;
-                }
-
-                 $signals_synop =~ s/^\n*//g;
-                 $signals_synop =~ s/\n+$/\n/g;
-                if ($signals_synop ne '') {
-                    $signals_synop = <<EOF;
-<refsect1 id="$section_id.signals" role="signal_proto">
+                if signals_synop != '':
+                    signals_synop = '''<refsect1 id="%s.signals" role="signal_proto">
 <title role="signal_proto.title">Signals</title>
 <informaltable frame="none">
 <tgroup cols="3">
@@ -718,26 +676,23 @@ sub OutputDB {
 <colspec colname="signals_name" colwidth="300px"/>
 <colspec colname="signals_flags" colwidth="200px"/>
 <tbody>
-${signals_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                     $signals_desc = TrimTextBlock($signals_desc);
-                    $signals_desc  = <<EOF;
-<refsect1 id="$section_id.signal-details" role="signals">
+''' % (section_id, signals_synop)
+                    signals_desc = TrimTextBlock(signals_desc)
+                    signals_desc = '''<refsect1 id="%s.signal-details" role="signals">
 <title role="signals.title">Signal Details</title>
-$signals_desc
+%s
 </refsect1>
-EOF
-                }
-
-                $args_synop =~ s/^\n*//g;
-                $args_synop =~ s/\n+$/\n/g;
-                if ($args_synop ne '') {
-                    $args_synop = <<EOF;
-<refsect1 id="$section_id.properties" role="properties">
+''' % (section_id, signals_desc)
+
+                args_synop = re.sub(r'^\n*', '', args_synop)
+                args_synop = re.sub(r'\n+$', '\n', args_synop)
+                if args_synop != '':
+                    args_synop = '''<refsect1 id="%s.properties" role="properties">
 <title role="properties.title">Properties</title>
 <informaltable frame="none">
 <tgroup cols="3">
@@ -745,26 +700,23 @@ EOF
 <colspec colname="properties_name" colwidth="300px"/>
 <colspec colname="properties_flags" colwidth="200px"/>
 <tbody>
-${args_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                     $args_desc = TrimTextBlock($args_desc);
-                    $args_desc  = <<EOF;
-<refsect1 id="$section_id.property-details" role="property_details">
+''' %(section_id, args_synop)
+                    args_desc = TrimTextBlock(args_desc)
+                    args_desc = '''<refsect1 id="%s.property-details" role="property_details">
 <title role="property_details.title">Property Details</title>
-$args_desc
+%s
 </refsect1>
-EOF
-                }
-
-                $child_args_synop =~ s/^\n*//g;
-                $child_args_synop =~ s/\n+$/\n/g;
-                if ($child_args_synop ne '') {
-                    $args_synop .= <<EOF;
-<refsect1 id="$section_id.child-properties" role="child_properties">
+''' % (section_id, args_desc)
+
+                child_args_synop = re.sub(r'^\n*', '', child_args_synop)
+                child_args_synop = re.sub(r'\n+$', '\n', child_args_synop)
+                if child_args_synop != '':
+                    args_synop += '''<refsect1 id="%s.child-properties" role="child_properties">
 <title role="child_properties.title">Child Properties</title>
 <informaltable frame="none">
 <tgroup cols="3">
@@ -772,26 +724,24 @@ EOF
 <colspec colname="child_properties_name" colwidth="300px"/>
 <colspec colname="child_properties_flags" colwidth="200px"/>
 <tbody>
-${child_args_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                     $child_args_desc = TrimTextBlock($child_args_desc);
-                     $args_desc .= <<EOF;
-<refsect1 id="$section_id.child-property-details" role="child_property_details">
+''' % (section_id, child_args_synop)
+                    child_args_desc = TrimTextBlock(child_args_desc)
+                    args_desc += '''<refsect1 id="%s.child-property-details" role="child_property_details">
 <title role="child_property_details.title">Child Property Details</title>
-$child_args_desc
+%s
 </refsect1>
-EOF
-                }
-
-                $style_args_synop =~ s/^\n*//g;
-                $style_args_synop =~ s/\n+$/\n/g;
-                if ($style_args_synop ne '') {
-                    $args_synop .= <<EOF;
-<refsect1 id="$section_id.style-properties" role="style_properties">
+''' % (section_id, child_args_desc)
+
+
+                style_args_synop = re.sub(r'^\n*', '', style_args_synop)
+                style_args_synop = re.sub(r'\n+$', '\n', style_args_synop)
+                if style_args_synop != '':
+                    args_synop += '''<refsect1 id="%s.style-properties" role="style_properties">
 <title role="style_properties.title">Style Properties</title>
 <informaltable frame="none">
 <tgroup cols="3">
@@ -799,265 +749,239 @@ EOF
 <colspec colname="style_properties_name" colwidth="300px"/>
 <colspec colname="style_properties_flags" colwidth="200px"/>
 <tbody>
-${style_args_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                     $style_args_desc = TrimTextBlock($style_args_desc);
-                    $args_desc .= <<EOF;
-<refsect1 id="$section_id.style-property-details" role="style_properties_details">
+''' % (section_id, style_args_synop)
+                    style_args_desc = TrimTextBlock(style_args_desc)
+                    args_desc += '''<refsect1 id="%s.style-property-details" role="style_properties_details">
 <title role="style_properties_details.title">Style Property Details</title>
-$style_args_desc
+%s
 </refsect1>
-EOF
-                }
+''' % (section_id, style_args_desc)
 
-                $hierarchy_str = &AddTreeLineArt(\@hierarchy);
-                if ($hierarchy_str ne "") {
-                    $hierarchy_str = <<EOF;
-<refsect1 id="$section_id.object-hierarchy" role="object_hierarchy">
+                hierarchy_str = AddTreeLineArt(hierarchy)
+                if hierarchy_str != '':
+                    hierarchy_str = '''<refsect1 id="%s.object-hierarchy" role="object_hierarchy">
 <title role="object_hierarchy.title">Object Hierarchy</title>
-<screen>$hierarchy_str
+<screen>%s
 </screen>
 </refsect1>
-EOF
-                }
+''' % (section_id, hierarchy_str)
 
-                 $interfaces =~ TrimTextBlock($interfaces);
-                if ($interfaces ne "") {
-                    $interfaces = <<EOF;
-<refsect1 id="$section_id.implemented-interfaces" role="impl_interfaces">
+                interfaces = TrimTextBlock(interfaces)
+                if interfaces != '':
+                    interfaces = '''<refsect1 id="%s.implemented-interfaces" role="impl_interfaces">
 <title role="impl_interfaces.title">Implemented Interfaces</title>
-$interfaces
+%s
 </refsect1>
-EOF
-                }
+''' % (section_id, interfaces)
 
-                 $implementations = TrimTextBlock($implementations);
-                if ($implementations ne "") {
-                    $implementations = <<EOF;
-<refsect1 id="$section_id.implementations" role="implementations">
+                implementations = TrimTextBlock(implementations)
+                if implementations != '':
+                    implementations = '''<refsect1 id="%s.implementations" role="implementations">
 <title role="implementations.title">Known Implementations</title>
-$implementations
+%s
 </refsect1>
-EOF
-                }
+''' % (section_id, implementations)
 
-                 $prerequisites = TrimTextBlock($prerequisites);
-                if ($prerequisites ne "") {
-                    $prerequisites = <<EOF;
-<refsect1 id="$section_id.prerequisites" role="prerequisites">
+                prerequisites = TrimTextBlock(prerequisites)
+                if prerequisites != '':
+                    prerequisites = '''<refsect1 id="%s.prerequisites" role="prerequisites">
 <title role="prerequisites.title">Prerequisites</title>
-$prerequisites
+%s
 </refsect1>
-EOF
-                }
+''' % (section_id, prerequisites)
 
-                 $derived = TrimTextBlock($derived);
-                if ($derived ne "") {
-                    $derived = <<EOF;
-<refsect1 id="$section_id.derived-interfaces" role="derived_interfaces">
+
+                derived = TrimTextBlock(derived)
+                if derived != '':
+                    derived = '''<refsect1 id="%s.derived-interfaces" role="derived_interfaces">
 <title role="derived_interfaces.title">Known Derived Interfaces</title>
-$derived
+%s
 </refsect1>
-EOF
-                }
-
-                $functions_synop =~ s/^\n*//g;
-                $functions_synop =~ s/\n+$/\n/g;
-                if ($functions_synop ne '') {
-                  $functions_synop = <<EOF;
-<refsect1 id="$section_id.functions" role="functions_proto">
+''' % (section_id, derived)
+
+                functions_synop = re.sub(r'^\n*', '', functions_synop)
+                functions_synop = re.sub(r'\n+$', '\n', functions_synop)
+                if functions_synop != '':
+                    functions_synop = '''<refsect1 id="%s.functions" role="functions_proto">
 <title role="functions_proto.title">Functions</title>
 <informaltable pgwide="1" frame="none">
 <tgroup cols="2">
 <colspec colname="functions_return" colwidth="150px"/>
 <colspec colname="functions_name"/>
 <tbody>
-${functions_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                }
-
-                $other_synop =~ s/^\n*//g;
-                $other_synop =~ s/\n+$/\n/g;
-                if ($other_synop ne '') {
-                  $other_synop = <<EOF;
-<refsect1 id="$section_id.other" role="other_proto">
+''' % (section_id, functions_synop)
+
+                other_synop = re.sub(r'^\n*', '', other_synop)
+                other_synop = re.sub(r'\n+$', '\n', other_synop)
+                if other_synop != '':
+                    other_synop = '''<refsect1 id="%s.other" role="other_proto">
 <title role="other_proto.title">Types and Values</title>
 <informaltable role="enum_members_table" pgwide="1" frame="none">
 <tgroup cols="2">
 <colspec colname="name" colwidth="150px"/>
 <colspec colname="description"/>
 <tbody>
-${other_synop}
+%s
 </tbody>
 </tgroup>
 </informaltable>
 </refsect1>
-EOF
-                }
-
-                my $file_changed = &OutputDBFile ($filename, $title, $section_id,
-                                                    $section_includes,
-                                                    \$functions_synop, \$other_synop,
-                                                    \$functions_details, \$other_details,
-                                                    \$signals_synop, \$signals_desc,
-                                                    \$args_synop, \$args_desc,
-                                                    \$hierarchy_str, \$interfaces,
-                                                    \$implementations,
-                                                    \$prerequisites, \$derived,
-                                                    \@file_objects);
-                if ($file_changed) {
-                    $changed = 1;
-                }
-            }
-            $title = "";
-            $section_id = "";
-            $subsection = "";
-            $in_section = 0;
-            $section_includes = "";
-            $functions_synop = "";
-            $other_synop = "";
-            $functions_details = "";
-            $other_details = "";
-            $signals_synop = "";
-            $signals_desc = "";
-            $args_synop = "";
-            $child_args_synop = "";
-            $style_args_synop = "";
-            $args_desc = "";
-            $child_args_desc = "";
-            $style_args_desc = "";
-            $hierarchy_str = "";
-            @hierarchy = ();
-            $interfaces = "";
-            $implementations = "";
-            $prerequisites = "";
-            $derived = "";
-
-        } elsif (m/^(\S+)/) {
-            my $symbol = $1;
-            @TRACE@("  Symbol: $symbol in subsection: $subsection\n");
+''' % (section_id, other_synop)
+
+                file_changed = OutputDBFile(filename, title, section_id,
+                                            section_includes,
+                                            functions_synop, other_synop,
+                                            functions_details, other_details,
+                                            signals_synop, signals_desc,
+                                            args_synop, args_desc,
+                                            hierarchy_str, interfaces,
+                                            implementations,
+                                            prerequisites, derived,
+                                            file_objects)
+                if file_changed:
+                    changed = True
+
+            title = ''
+            section_id = ''
+            subsection = ''
+            in_section = 0
+            section_includes = ''
+            functions_synop = ''
+            other_synop = ''
+            functions_details = ''
+            other_details = ''
+            signals_synop = ''
+            signals_desc = ''
+            args_synop = ''
+            child_args_synop = ''
+            style_args_synop = ''
+            args_desc = ''
+            child_args_desc = ''
+            style_args_desc = ''
+            hierarchy_str = ''
+            hierarchy = []
+            interfaces = ''
+            implementations = ''
+            prerequisites = ''
+            derived = ''
+
+        elif m5:
+            symbol = m5.group(1)
+            logging.info("  Symbol: %s in subsection: %s", symbol, subsection)
 
             # check for duplicate entries
-            if (! defined $symbol_def_line{$symbol}) {
-                my $declaration = $Declarations{$symbol};
-                if (defined ($declaration)) {
-                    if (&CheckIsObject ($symbol)) {
-                        push @file_objects, $symbol;
-                    }
+            if symbol in symbol_def_line:
+                declaration = Declarations.get(symbol, None)
+                if declaration:
+                    if CheckIsObject(symbol):
+                        file_objects.append(symbol)
+
                     # We don't want standard macros/functions of GObjects,
                     # or private declarations.
-                    if ($subsection ne "Standard" && $subsection ne "Private") {
-                        my ($synop, $desc) = &OutputDeclaration ($symbol,
-                                                                 $declaration);
-                        my $type = $DeclarationTypes {$symbol};
-
-                        if ($type eq 'FUNCTION' || $type eq 'USER_FUNCTION') {
-                          $functions_synop .= $synop;
-                          $functions_details .= $desc;
-                        } elsif ($type eq 'MACRO' && $declaration =~ /$symbol\(/) {
-                          $functions_synop .= $synop;
-                          $functions_details .= $desc;
-                        } else {
-                          $other_synop .= $synop;
-                          $other_details .= $desc;
-                        }
-                    }
-                    my ($sig_synop, $sig_desc) = &GetSignals ($symbol);
-                    my ($arg_synop, $child_arg_synop, $style_arg_synop,
-                        $arg_desc, $child_arg_desc, $style_arg_desc) = &GetArgs ($symbol);
-                    my $ifaces = &GetInterfaces ($symbol);
-                    my $impls = &GetImplementations ($symbol);
-                    my $prereqs = &GetPrerequisites ($symbol);
-                    my $der = &GetDerived ($symbol);
-                    @hierarchy = &GetHierarchy ($symbol, \@hierarchy);
-
-                    $signals_synop .= $sig_synop;
-                    $signals_desc .= $sig_desc;
-                    $args_synop .= $arg_synop;
-                    $child_args_synop .= $child_arg_synop;
-                    $style_args_synop .= $style_arg_synop;
-                    $args_desc .= $arg_desc;
-                    $child_args_desc .= $child_arg_desc;
-                    $style_args_desc .= $style_arg_desc;
-                    $interfaces .= $ifaces;
-                    $implementations .= $impls;
-                    $prerequisites .= $prereqs;
-                    $derived .= $der;
+                    if subsection != "Standard" and subsection != "Private":
+                        synop, desc = OutputDeclaration(symbol, declaration)
+                        type = DeclarationTypes[symbol]
+
+                        if type == 'FUNCTION' or type == 'USER_FUNCTION':
+                            functions_synop += synop
+                            functions_details += desc
+                        elif type == 'MACRO' and re.search(symbol + r'\(', declaration):
+                            functions_synop += synop
+                            functions_details += desc
+                        else:
+                            other_synop += synop
+                            other_details += +desc
+
+                    sig_synop, sig_desc = GetSignals(symbol)
+                    arg_synop, child_arg_synop, style_arg_synop, arg_desc, child_arg_desc, style_arg_desc = 
GetArgs(symbol)
+                    ifaces = GetInterfaces(symbol)
+                    impls = GetImplementations(symbol)
+                    prereqs = GetPrerequisites(symbol)
+                    der = GetDerived(symbol)
+                    hierarchy = GetHierarchy(symbol, hierarchy)
+
+                    signals_synop += sig_synop
+                    signals_desc += sig_desc
+                    args_synop += arg_synop
+                    child_args_synop += child_arg_synop
+                    style_args_synop += style_arg_synop
+                    args_desc += arg_desc
+                    child_args_desc += child_arg_desc
+                    style_args_desc += style_arg_desc
+                    interfaces += ifaces
+                    implementations += impls
+                    prerequisites += prereqs
+                    derived += der
 
                     # Note that the declaration has been output.
-                    $DeclarationOutput{$symbol} = 1;
-                } elsif ($subsection ne "Standard" && $subsection ne "Private") {
-                    $UndeclaredSymbols{$symbol} = 1;
-                    &LogWarning ($file, $., "No declaration found for $symbol.");
-                }
-                $num_symbols++;
-                $symbol_def_line{$symbol}=$.;
-
-                if ($section_id eq "") {
-                    if($title eq "" && $filename eq "") {
-                        &LogWarning ($file, $., "Section has no title and no file.");
-                    }
+                    DeclarationOutput[symbol] = True
+                elif subsection != "Standard" and subsection != "Private":
+                    UndeclaredSymbols[symbol] = True
+                    common.LogWarning(file, line_number, "No declaration found for %s." % symbol)
+
+                num_symbols += 1
+                symbol_def_line[symbol] = line_number
+
+                if section_id == '':
+                    if title == '' and filename == '':
+                        common.LogWarning(file, line_number, "Section has no title and no file.")
+
                     # FIXME: one of those would be enough
                     # filename should be an internal detail for gtk-doc
-                    if ($title eq "") {
-                        $title = $filename;
-                    } elsif ($filename eq "") {
-                        $filename = $title;
-                    }
-                    $filename =~ s/\s/_/g;
+                    if title == '':
+                        title = filename
+                    elif filename == '':
+                        filename = title
+
+                    filename = filename.replace(' ', '_')
 
-                    $section_id = $SourceSymbolDocs{"$TMPL_DIR/$filename:Section_Id"};
-                    if (defined ($section_id) && $section_id !~ m/^\s*$/) {
+                    section_id = SourceSymbolDocs[os.path.join(TMPL_DIR, filename + ":Section_Id")]
+                    if section_id and section_id.strip() != '':
                         # Remove trailing blanks and use as is
-                        $section_id =~ s/\s+$//;
-                    } elsif (&CheckIsObject ($title)) {
+                        section_id = section_id.rstrip()
+                    elif CheckIsObject(title):
                         # GObjects use their class name as the ID.
-                        $section_id = &CreateValidSGMLID ($title);
-                    } else {
-                        $section_id = &CreateValidSGMLID ("$MODULE-$title");
-                    }
-                }
-                $SymbolSection{$symbol}=$title;
-                $SymbolSectionId{$symbol}=$section_id;
-            }
-            else {
-                &LogWarning ($file, $., "Double symbol entry for $symbol. ".
-                    "Previous occurrence on line ".$symbol_def_line{$symbol}.".");
-            }
-        }
-    }
-    close (INPUT);
-
-    &OutputMissingDocumentation;
-    &OutputUndeclaredSymbols;
-    &OutputUnusedSymbols;
-
-    if ($OUTPUT_ALL_SYMBOLS) {
-        &OutputAllSymbols;
-    }
-    if ($OUTPUT_SYMBOLS_WITHOUT_SINCE) {
-        &OutputSymbolsWithoutSince;
-    }
-
-    for $filename (split (' ', $EXPAND_CONTENT_FILES)) {
-        my $file_changed = &OutputExtraFile ($filename);
-        if ($file_changed) {
-            $changed = 1;
-        }
-    }
-
-    &OutputBook ($book_top, $book_bottom);
-
-    return $changed;
-}
+                        section_id = common.CreateValidSGMLID(title)
+                    else:
+                        section_id = common.CreateValidSGMLID(MODULE + '-' + title)
+
+                SymbolSection[symbol] = title
+                SymbolSectionId[symbol] = section_id
+
+            else:
+                common.LogWarning(file, line_number, "Double symbol entry for %s. "
+                                  "Previous occurrence on line %d." % (symbol, symbol_def_line[symbol]))
+    INPUT.close()
+
+    OutputMissingDocumentation()
+    OutputUndeclaredSymbols()
+    OutputUnusedSymbols()
+
+    if OUTPUT_ALL_SYMBOLS:
+        OutputAllSymbols()
+
+    if OUTPUT_SYMBOLS_WITHOUT_SINCE:
+        OutputSymbolsWithoutSince()
+
+
+    for filename in EXPAND_CONTENT_FILES.split():
+        file_changed = OutputExtraFile(filename)
+        if file_changed:
+            changed = True
+
+    OutputBook(book_top, book_bottom)
+
+    return changed
 
 #############################################################################
 # Function    : OutputIndex
@@ -1065,120 +989,110 @@ EOF
 #               document into an <index> tag.
 #############################################################################
 
-sub OutputIndex {
-    my ($basename, $apiindexref ) = @_;
-    my %apiindex = %{$apiindexref};
-    my $old_index = "$DB_OUTPUT_DIR/$basename.xml";
-    my $new_index = "$DB_OUTPUT_DIR/$basename.new";
-    my $lastletter = " ";
-    my $divopen = 0;
-    my $symbol;
-    my $short_symbol;
+def OutputIndex(basename, apiindexref):
+    apiindex = apiindexref # FIXME should we copy here?
+    old_index = os.path.join(DB_OUTPUT_DIR, basename + '.xml')
+    new_index = os.path.join(DB_OUTPUT_DIR, basename + '.new')
+    lastletter = " "
+    divopen = 0
+    symbol = None
+    short_symbol = None
 
-    open (OUTPUT, ">$new_index")
-        || die "Can't create $new_index";
+    OUTPUT = open(new_index, 'w')
 
-    print (OUTPUT &MakeDocHeader ("indexdiv")."\n<indexdiv id=\"$basename\">\n");
+    OUTPUT.write(MakeDocHeader("indexdiv") + "\n<indexdiv id=\"%s\">\n" % basename)
 
-    @TRACE@("generate $basename index (".%apiindex." entries)\n");
+    logging.info("generate %s index (%d entries)\n", basename, len(apiindex))
 
     # do a case insensitive sort while chopping off the prefix
-    foreach my $hash (
-        sort { $$a{criteria} cmp $$b{criteria} or $$a{original} cmp $$b{original} }
-        map { my $x = uc($_); $x =~ s/^$NAME_SPACE\_?(.*)/$1/i; { criteria => $x, original => $_, short => 
$1 } }
-        keys %apiindex) {
-
-        $symbol = $$hash{original};
-        if (defined($$hash{short}) && $$hash{short} ne "") {
-            $short_symbol = $$hash{short};
-        } else {
-            $short_symbol = $symbol;
-        }
+    def cpmfunc(intxt):
+        uc = intxt.upper()
+        criteria = re.sub(r'^' + NAME_SPACE + r'\_?(.*)', r'\1', uc, flags=re.I)
+        return (criteria, intxt)
+
+    sorted_keys = sorted(apiindex.keys(), key=cpmfunc)
+
+    for original in sorted_keys:
+        symbol = apiindex[original]
+        match = re.search(r'^' + NAME_SPACE + r'\_?(.*)', original, flags=re.I)
+        short = match.group(1)
+        if short and short != '':
+            short_symbol = short
+        else:
+            short_symbol = symbol
 
         # generate a short symbol description
-        my $symbol_desc = "";
-        my $symbol_section = "";
-        my $symbol_section_id = "";
-        my $symbol_type = "";
-        if (defined($DeclarationTypes{$symbol})) {
-          $symbol_type = lc($DeclarationTypes{$symbol});
-        }
-        if ($symbol_type eq "") {
-            @TRACE@("trying symbol $symbol\n");
-            if ($symbol =~ m/(.*)::(.*)/) {
-                my $oname = $1;
-                my $osym = $2;
-                my $i;
-                @TRACE@("  trying object signal ${oname}:$osym in ".$#SignalNames." signals\n");
-                for ($i = 0; $i <= $#SignalNames; $i++) {
-                    if ($SignalNames[$i] eq $osym) {
-                        $symbol_type = "object signal";
-                        if (defined($SymbolSection{$oname})) {
-                           $symbol_section = $SymbolSection{$oname};
-                           $symbol_section_id = $SymbolSectionId{$oname};
-                        }
-                        last;
-                    }
-                }
-            } elsif ($symbol =~ m/(.*):(.*)/) {
-                my $oname = $1;
-                my $osym = $2;
-                my $i;
-                @TRACE@("  trying object property ${oname}::$osym in ".$#ArgNames." properties\n");
-                for ($i = 0; $i <= $#ArgNames; $i++) {
-                    @TRACE@("    ".$ArgNames[$i]."\n");
-                    if ($ArgNames[$i] eq $osym) {
-                        $symbol_type = "object property";
-                        if (defined($SymbolSection{$oname})) {
-                           $symbol_section = $SymbolSection{$oname};
-                           $symbol_section_id = $SymbolSectionId{$oname};
-                        }
-                        last;
-                    }
-                }
-            }
-        } else {
-           if (defined($SymbolSection{$symbol})) {
-               $symbol_section = $SymbolSection{$symbol};
-               $symbol_section_id = $SymbolSectionId{$symbol};
-           }
-        }
-        if ($symbol_type ne "") {
-           $symbol_desc=", $symbol_type";
-           if ($symbol_section ne "") {
-               $symbol_desc.=" in <link linkend=\"$symbol_section_id\">$symbol_section</link>";
-               #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section");
-           }
-        }
-
-        my $curletter = uc(substr($short_symbol,0,1));
-        my $id = $apiindex{$symbol};
-
-        @TRACE@("  add symbol $symbol with $id to index in section '$curletter' (derived from 
$short_symbol)\n");
-
-        if ($curletter ne $lastletter) {
-            $lastletter = $curletter;
-
-            if ($divopen == 1) {
-                print (OUTPUT "</indexdiv>\n");
-            }
-            print (OUTPUT "<indexdiv><title>$curletter</title>\n");
-            $divopen = 1;
-        }
-
-        print (OUTPUT <<EOF);
-<indexentry><primaryie linkends="$id"><link linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>
-EOF
-    }
-
-    if ($divopen == 1) {
-        print (OUTPUT "</indexdiv>\n");
-    }
-    print (OUTPUT "</indexdiv>\n");
-    close (OUTPUT);
-
-    &UpdateFileIfChanged ($old_index, $new_index, 0);
-}
+        symbol_desc = ''
+        symbol_section = ''
+        symbol_section_id = ''
+        symbol_type = ''
+        if symbol in DeclarationTypes:
+            symbol_type = DeclarationTypes[symbol].lower()
+
+        if symbol_type == '':
+            logging.info("trying symbol %s\n", symbol)
+            m = re.search(r'(.*)::(.*)', symbol)
+            m2 = re.search(r'(.*):(.*)/', symbol)
+            if m:
+                oname = m.group(1)
+                osym = m.group(2)
+                i = None
+                logging.info("  trying object signal %s:%s in %d signals\n", oname, osym, len(SignalNames))
+                for i in range(len(SignalNames)):
+                    if SignalNames[i] == osym:
+                        symbol_type = "object signal"
+                        if oname in SymbolSection:
+                            symbol_section = SymbolSection[oname]
+                            symbol_section_id = SymbolSectionId[oname]
+                        break
+            elif m2:
+                oname = m2.group(1)
+                osym = m2.group(2)
+                i = None
+                logging.info("  trying object property %s::%s in %d properties\n", oname, osym, 
len(ArgNames))
+                for i in range(len(ArgNames)):
+                    logging.info("    " + ArgNames[i] + "\n")
+                    if ArgNames[i] == osym:
+                        symbol_type = "object property"
+                        if oname in SymbolSection:
+                            symbol_section = SymbolSection[oname]
+                            symbol_section_id = SymbolSectionId[oname]
+                        break
+        else:
+            if symbol in SymbolSection:
+                symbol_section = SymbolSection[symbol]
+                symbol_section_id = SymbolSectionId[symbol]
+
+        if symbol_type != '':
+            symbol_desc = ", " + symbol_type
+            if symbol_section != '':
+                symbol_desc += " in <link linkend=\"%s\">%s</link>" % (symbol_section_id, symbol_section)
+                #$symbol_desc.=" in ". &ExpandAbbreviations($symbol, "#$symbol_section")
+
+        curletter = short_symbol[0].upper()
+        id = apiindex[symbol]
+
+        logging.info("  add symbol %s with %d to index in section '%s' (derived from %s)\n", symbol, id, 
curletter, short_symbol)
+
+        if curletter != lastletter:
+            lastletter = curletter
+
+            if divopen:
+                OUTPUT.write("</indexdiv>\n")
+
+            OUTPUT.write("<indexdiv><title>$curletter</title>\n")
+            divopen = True
+
+        OUTPUT.write('<indexentry><primaryie linkends="$id"><link 
linkend="$id">$symbol</link>$symbol_desc</primaryie></indexentry>\n')
+
+    if divopen:
+        OUTPUT.write("</indexdiv>\n")
+
+    OUTPUT.write("</indexdiv>\n")
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_index, new_index, 0)
+
 
 
 #############################################################################
@@ -1187,9 +1101,8 @@ EOF
 #               main document into an <index> tag.
 #############################################################################
 
-sub OutputIndexFull {
-    &OutputIndex ("api-index-full", \%IndexEntriesFull);
-}
+def OutputIndexFull():
+    OutputIndex("api-index-full", IndexEntriesFull)
 
 
 #############################################################################
@@ -1198,9 +1111,8 @@ sub OutputIndexFull {
 #               into the main document into an <index> tag.
 #############################################################################
 
-sub OutputDeprecatedIndex {
-    &OutputIndex ("api-index-deprecated", \%IndexEntriesDeprecated);
-}
+def OutputDeprecatedIndex():
+    OutputIndex("api-index-deprecated", IndexEntriesDeprecated)
 
 
 #############################################################################
@@ -1209,18 +1121,19 @@ sub OutputDeprecatedIndex {
 #               the main document into an <index> tag.
 #############################################################################
 
-sub OutputSinceIndexes {
-    my @sinces = keys %{{ map { $_ => 1 } values %Since }};
+def OutputSinceIndexes():
+    raise RuntimeError('I have no idea what this does.')
+#    my @sinces = keys %{{ map { $_ => 1 } values %Since }
+#
+#    foreach my $version (@sinces)
+#        logging.info("Since : [$version]\n")
+#        # TODO make filtered hash
+#        #my %index = grep { $Since{$_} == $version } %IndexEntriesSince
+#        my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} == $version } keys 
%IndexEntriesSince
+#
+#        &OutputIndex ("api-index-$version", \%index)
 
-    foreach my $version (@sinces) {
-        @TRACE@("Since : [$version]\n");
-        # TODO make filtered hash
-        #my %index = grep { $Since{$_} eq $version } %IndexEntriesSince;
-        my %index = map { $_ => $IndexEntriesSince{$_} } grep { $Since{$_} eq $version } keys 
%IndexEntriesSince;
 
-        &OutputIndex ("api-index-$version", \%index);
-    }
-}
 
 #############################################################################
 # Function    : OutputAnnotationGlossary
@@ -1229,70 +1142,64 @@ sub OutputSinceIndexes {
 #               document.
 #############################################################################
 
-sub OutputAnnotationGlossary {
-    my $old_glossary = "$DB_OUTPUT_DIR/annotation-glossary.xml";
-    my $new_glossary = "$DB_OUTPUT_DIR/annotation-glossary.new";
-    my $lastletter = " ";
-    my $divopen = 0;
+def OutputAnnotationGlossary():
+    old_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.xml")
+    new_glossary = os.path.join(DB_OUTPUT_DIR, "annotation-glossary.new")
+    lastletter = " "
+    divopen = False
 
     # if there are no annotations used return
-    return if (! keys(%AnnotationsUsed));
+    if len(AnnotationsUsed) == 0:
+        return
 
     # add acronyms that are referenced from acronym text
-rerun:
-    foreach my $annotation (keys(%AnnotationsUsed)) {
-        if(defined($AnnotationDefinition{$annotation})) {
-            if($AnnotationDefinition{$annotation} =~ m/<acronym>([\w ]+)<\/acronym>/) {
-                if (!exists($AnnotationsUsed{$1})) {
-                    $AnnotationsUsed{$1} = 1;
-                    goto rerun;
-                }
-            }
-        }
-    }
-
-    open (OUTPUT, ">$new_glossary")
-        || die "Can't create $new_glossary";
-
-    print (OUTPUT  <<EOF);
-${\( MakeDocHeader ("glossary") )}
+#rerun:
+    for annotation in AnnotationsUsed:
+        if annotation in AnnotationDefinition:
+            m = re.search(r'<acronym>([\w ]+)<\/acronym>', AnnotationDefinition[annotation])
+            if m:
+                if m.group(1) in AnnotationsUsed:
+                    AnnotationsUsed[m.group(1)] = 1
+                    return OutputAnnotationGlossary()
+
+    OUTPUT = open(new_glossary, 'w')
+
+    OUTPUT.write('''%s
 <glossary id="annotation-glossary">
   <title>Annotation Glossary</title>
-EOF
-
-    foreach my $annotation (sort({lc $a cmp lc $b} keys(%AnnotationsUsed))) {
-        if(defined($AnnotationDefinition{$annotation})) {
-            my $def = $AnnotationDefinition{$annotation};
-            my $curletter = uc(substr($annotation,0,1));
-
-            if ($curletter ne $lastletter) {
-                $lastletter = $curletter;
-
-                if ($divopen == 1) {
-                    print (OUTPUT "</glossdiv>\n");
-                }
-                print (OUTPUT "<glossdiv><title>$curletter</title>\n");
-                $divopen = 1;
-            }
-            print (OUTPUT <<EOF);
+''' % MakeDocHeader("glossary"))
+
+    for annotation in sorted(AnnotationsUsed.keys(), keys=str.lower()):
+        if annotation in AnnotationDefinition:
+            defi = AnnotationDefinition[annotation]
+            curletter = annotation[0].upper()
+
+            if curletter != lastletter:
+                lastletter = curletter
+
+                if divopen:
+                    OUTPUT.write("</glossdiv>\n")
+
+                OUTPUT.write("<glossdiv><title>%s</title>\n" % curletter)
+                divopen = True
+
+            OUTPUT.write('''
     <glossentry>
-      <glossterm><anchor id="annotation-glossterm-$annotation"/>$annotation</glossterm>
+      <glossterm><anchor id="annotation-glossterm-%s"/>%s</glossterm>
       <glossdef>
-        <para>$def</para>
+        <para>%s</para>
       </glossdef>
     </glossentry>
-EOF
-        }
-    }
+''' % (annotation, annotation, defi))
 
-    if ($divopen == 1) {
-        print (OUTPUT "</glossdiv>\n");
-    }
-    print (OUTPUT "</glossary>\n");
-    close (OUTPUT);
+    if divopen:
+        OUTPUT.write("</glossdiv>\n")
+
+    OUTPUT.write("</glossary>\n")
+    OUTPUT.close()
+
+    common.UpdateFileIfChanged(old_glossary, new_glossary, 0)
 
-    &UpdateFileIfChanged ($old_glossary, $new_glossary, 0);
-}
 
 #############################################################################
 # Function    : ReadKnownSymbols
@@ -1303,56 +1210,56 @@ EOF
 #                into sections and subsections.
 #############################################################################
 
-sub ReadKnownSymbols {
-    my ($file) = @_;
+def ReadKnownSymbols(file):
 
-    my $subsection = "";
+    subsection = ''
 
-    @TRACE@("Reading: $file\n");
-    open (INPUT, $file)
-        || die "Can't open $file: $!";
+    logging.info("Reading: %s", file)
+    INPUT = open(file)
 
-    while (<INPUT>) {
-        if (m/^#/) {
-            next;
+    for line in INPUT:
+        if line.strip() == '':
+            continue
 
-        } elsif (m/^<SECTION>/) {
-            $subsection = "";
+        m = re.search(r'^<SECTION>', line)
+        if m:
+            subsection = ''
+            continue
 
-        } elsif (m/^<SUBSECTION\s*(.*)>/i) {
-            $subsection = $1;
+        m = re.search(r'^<SUBSECTION\s*(.*)', line, flags=re.I)
+        if m:
+            subsection = m.group(1)
+            continue
 
-        } elsif (m/^<SUBSECTION>/) {
-            next;
+        if re.search(r'^<SUBSECTION>', line):
+            continue
 
-        } elsif (m/^<TITLE>(.*)<\/TITLE>/) {
-            next;
+        if re.search(r'^<TITLE>(.*)<\/TITLE>', line):
+            continue
 
-        } elsif (m/^<FILE>(.*)<\/FILE>/) {
-            $KnownSymbols{"$TMPL_DIR/$1:Long_Description"} = 1;
-            $KnownSymbols{"$TMPL_DIR/$1:Short_Description"} = 1;
-            next;
+        m = re.search(r'^<FILE>(.*)<\/FILE>', line)
+        if m:
+            KnownSymbols[os.path.join(TMPL_DIR, m.group(1) + ":Long_Description")] = 1
+            KnownSymbols[os.path.join(TMPL_DIR, m.group(1) + ":Short_Description")] = 1
+            continue
 
-        } elsif (m/^<INCLUDE>(.*)<\/INCLUDE>/) {
-            next;
+        m = re.search(r'^<INCLUDE>(.*)<\/INCLUDE>', line)
+        if m:
+            continue
 
-        } elsif (m/^<\/SECTION>/) {
-            next;
+        m = re.search(r'^<\/SECTION>', line)
+        if m:
+            continue
 
-        } elsif (m/^(\S+)/) {
-            my $symbol = $1;
-
-            if ($subsection ne "Standard" && $subsection ne "Private") {
-                $KnownSymbols{$symbol} = 1;
-            }
-            else {
-                $KnownSymbols{$symbol} = 0;
-            }
-        }
-    }
-    close (INPUT);
-}
+        m = re.search(r'^(\S+)', line)
+        if m:
+            symbol = m.group(1)
 
+            if subsection != "Standard" and subsection != "Private":
+                KnownSymbols[symbol] = 1
+            else:
+                KnownSymbols[symbol] = 0
+    INPUT.close()
 
 #############################################################################
 # Function    : OutputDeclaration
@@ -1362,30 +1269,26 @@ sub ReadKnownSymbols {
 #                $declaration - the declaration of the function/macro.
 #############################################################################
 
-sub OutputDeclaration {
-    my ($symbol, $declaration) = @_;
-
-    my $type = $DeclarationTypes {$symbol};
-    if ($type eq 'MACRO') {
-        return &OutputMacro ($symbol, $declaration);
-    } elsif ($type eq 'TYPEDEF') {
-        return &OutputTypedef ($symbol, $declaration);
-    } elsif ($type eq 'STRUCT') {
-        return &OutputStruct ($symbol, $declaration);
-    } elsif ($type eq 'ENUM') {
-        return &OutputEnum ($symbol, $declaration);
-    } elsif ($type eq 'UNION') {
-        return &OutputUnion ($symbol, $declaration);
-    } elsif ($type eq 'VARIABLE') {
-        return &OutputVariable ($symbol, $declaration);
-    } elsif ($type eq 'FUNCTION') {
-        return &OutputFunction ($symbol, $declaration, $type);
-    } elsif ($type eq 'USER_FUNCTION') {
-        return &OutputFunction ($symbol, $declaration, $type);
-    } else {
-        die "Unknown symbol type";
-    }
-}
+def OutputDeclaration(symbol, declaration):
+    dtype = DeclarationTypes[symbol]
+    if dtype == 'MACRO':
+        return OutputMacro(symbol, declaration)
+    elif dtype == 'TYPEDEF':
+        return OutputTypedef(symbol, declaration)
+    elif dtype == 'STRUCT':
+        return OutputStruct(symbol, declaration)
+    elif dtype == 'ENUM':
+        return OutputEnum(symbol, declaration)
+    elif dtype == 'UNION':
+        return OutputUnion(symbol, declaration)
+    elif dtype == 'VARIABLE':
+        return OutputVariable(symbol, declaration)
+    elif dtype == 'FUNCTION':
+        return OutputFunction(symbol, declaration, dtype)
+    elif dtype == 'USER_FUNCTION':
+        return OutputFunction(symbol, declaration, dtype)
+    else:
+        sys.exit("Unknown symbol type " + dtype)
 
 
 #############################################################################
@@ -1394,21 +1297,19 @@ sub OutputDeclaration {
 # Arguments   : $symbol - the name of the function/macro begin described.
 #############################################################################
 
-sub OutputSymbolTraits {
-    my ($symbol) = @_;
-    my $desc = "";
-
-    if (exists $Since{$symbol}) {
-        my $link_id = "api-index-".$Since{$symbol};
-        $desc .= "<para role=\"since\">Since: <link linkend=\"$link_id\">$Since{$symbol}</link></para>";
-    }
-    if (exists $StabilityLevel{$symbol}) {
-        my $stability = $StabilityLevel{$symbol};
-        $AnnotationsUsed{$stability} = 1;
-        $desc .= "<para role=\"stability\">Stability Level: <acronym>$stability</acronym></para>";
-    }
-    return $desc;
-}
+def OutputSymbolTraits(symbol):
+    desc = ''
+
+    if symbol in Since:
+        link_id = "api-index-" + Since[symbol]
+        desc += "<para role=\"since\">Since: <link linkend=\"%s\">%s</link></para>" % (link_id, 
Since[symbol])
+
+    if symbol in StabilityLevel:
+        stability = StabilityLevel[symbol]
+        AnnotationsUsed[stability] = True
+        desc += "<para role=\"stability\">Stability Level: <acronym>%s</acronym></para>" % stability
+    return desc
+
 
 #############################################################################
 # Function    : Output{Symbol,Section}ExtraLinks
@@ -1416,52 +1317,46 @@ sub OutputSymbolTraits {
 # Arguments   : $symbol - the name of the function/macro begin described.
 #############################################################################
 
-sub uri_escape {
-    my $text = $_[0];
-    return undef unless defined $text;
+def uri_escape(text):
+    if text is None:
+        return None
 
     # Build a char to hex map
-    my %escapes = ();
-    for (0..255) {
-            $escapes{chr($_)} = sprintf("%%%02X", $_);
-    }
+    escapes = {}
+    for i in range(256):
+        escapes[chr(i)] = "%%%02X" % i
 
     # Default unsafe characters.  RFC 2732 ^(uric - reserved)
-    $text =~ s/([^A-Za-z0-9\-_.!~*'()])/$escapes{$1}/g;
+    def do_escape(char):
+        return escapes[char]
+    text = re.sub(r"([^A-Za-z0-9\-_.!~*'()]", do_escape, text)
 
-    return $text;
-}
+    return text
 
-sub OutputSymbolExtraLinks {
-    my ($symbol) = @_;
-    my $desc = "";
-
-    if (0) { # NEW FEATURE: needs configurability
-    my $sstr = &uri_escape($symbol);
-    my $mstr = &uri_escape($MODULE);
-    $desc .= <<EOF;
-<ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr";>code search</ulink>
-<ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$sstr";>edit 
documentation</ulink>
-EOF
-    }
-    return $desc;
-}
+def OutputSymbolExtraLinks(symbol):
+    desc = ''
 
-sub OutputSectionExtraLinks {
-    my ($symbol,$docsymbol) = @_;
-    my $desc = "";
-
-    if (0) { # NEW FEATURE: needs configurability
-    my $sstr = &uri_escape($symbol);
-    my $mstr = &uri_escape($MODULE);
-    my $dsstr = &uri_escape($docsymbol);
-    $desc .= <<EOF;
-<ulink role="extralinks" url="http://www.google.com/codesearch?q=$sstr";>code search</ulink>
-<ulink role="extralinks" url="http://library.gnome.org/edit?module=$mstr&amp;symbol=$dsstr";>edit 
documentation</ulink>
-EOF
-    }
-    return $desc;
-}
+    if False: # NEW FEATURE: needs configurability
+        sstr = uri_escape(symbol)
+        mstr = uri_escape(MODULE)
+        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
+<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
+''' % (sstr, mstr, sstr)
+
+    return desc
+
+
+def OutputSectionExtraLinks(symbol, docsymbol):
+    desc = ''
+
+    if False: # NEW FEATURE: needs configurability
+        sstr = uri_escape(symbol)
+        mstr = uri_escape(MODULE)
+        dsstr = uri_escape(docsymbol)
+        desc += '''<ulink role="extralinks" url="http://www.google.com/codesearch?q=%s";>code search</ulink>
+<ulink role="extralinks" url="http://library.gnome.org/edit?module=%s&amp;symbol=%s";>edit 
documentation</ulink>
+''' % (sstr, mstr, dsstr)
+    return desc
 
 
 #############################################################################
@@ -1471,60 +1366,59 @@ EOF
 #                $declaration - the declaration of the macro.
 #############################################################################
 
-sub OutputMacro {
-    my ($symbol, $declaration) = @_;
-    my $id = &CreateValidSGMLID ($symbol);
-    my $condition = &MakeConditionDescription ($symbol);
-    my $synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link 
linkend=\"$id\">$symbol</link>";
-    my $desc;
+def OutputMacro(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+    synop = "<row><entry role=\"define_keyword\">#define</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link>" % (sid, symbol)
+    desc = None
 
-    my @fields = ParseMacroDeclaration($declaration, \&CreateValidSGML);
-    my $title = $symbol . (@fields ? "()" : "");
+    fields = common.ParseMacroDeclaration(declaration, CreateValidSGML)
+    title = symbol  +'()' if len(fields) > 0 else ''
 
-    $desc = "<refsect2 id=\"$id\" role=\"macro\"$condition>\n<title>$title</title>\n";
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
+    desc = "<refsect2 id=\"%s\" role=\"macro\"condition>\n<title>title</title>\n" % (sid, condition, title)
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
 
-    if (@fields) {
-        $synop .= "<phrase role=\"c_punctuation\">()</phrase>";
-    }
-    $synop .= "</entry></row>\n";
+    if len(fields) > 0:
+        synop += "<phrase role=\"c_punctuation\">()</phrase>"
+
+    synop += "</entry></row>\n"
 
     # Don't output the macro definition if is is a conditional macro or it
     # looks like a function, i.e. starts with "g_" or "_?gnome_", or it is
     # longer than 2 lines, otherwise we get lots of complicated macros like
     # g_assert.
-    if (!defined ($DeclarationConditional{$symbol}) && ($symbol !~ m/^g_/)
-        && ($symbol !~ m/^_?gnome_/) && (($declaration =~ tr/\n//) < 2)) {
-        my $decl_out = &CreateValidSGML ($declaration);
-        $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
-    } else {
-        $desc .= "<programlisting language=\"C\">" . &MakeReturnField("#define") . "$symbol";
-        if ($declaration =~ m/^\s*#\s*define\s+\w+(\([^\)]*\))/) {
-            my $args = $1;
-            my $pad = ' ' x ($RETURN_TYPE_FIELD_WIDTH - length ("#define "));
+    if symbol in DeclarationConditional and not symbol.startswith('g_') \
+        and not re.search(r'^_?gnome_', symbol) and declaration.count('\n/') < 2:
+        decl_out = CreateValidSGML(declaration)
+        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+    else:
+        desc += "<programlisting language=\"C\">" + MakeReturnField("#define") + symbol
+        m = re.search(r'^\s*#\s*define\s+\w+(\([^\)]*\))', declaration)
+        if m:
+            args = m.group(1)
+            pad = ' ' * RETURN_TYPE_FIELD_WIDTH - len("#define ")
             # Align each line so that if should all line up OK.
-            $args =~ s/\n/\n$pad/gm;
-            $desc .= &CreateValidSGML ($args);
-        }
-        $desc .= "</programlisting>\n";
-    }
+            args = args.replace('\n', '\n' + pad)
+            desc += CreateValidSGML(args)
 
-    $desc .= &MakeDeprecationNote($symbol);
+        desc += "</programlisting>\n"
 
-    my $parameters = &OutputParamDescriptions ("MACRO", $symbol, @fields);
 
-    if (defined ($SymbolDocs{$symbol})) {
-        my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-        $desc .= $symbol_docs;
-    }
+    desc += MakeDeprecationNote(symbol)
 
-    $desc .= $parameters;
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+    parameters = OutputParamDescriptions("MACRO", symbol, fields)
+
+    if symbol in SymbolDocs:
+        symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
+        desc += symbol_docs
+
+
+    desc += parameters
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
 
 
 #############################################################################
@@ -1535,31 +1429,30 @@ sub OutputMacro {
 #                  e.g. 'typedef unsigned int guint;'
 #############################################################################
 
-sub OutputTypedef {
-    my ($symbol, $declaration) = @_;
-    my $id = &CreateValidSGMLID ($symbol);
-    my $condition = &MakeConditionDescription ($symbol);
-    my $desc = "<refsect2 id=\"$id\" role=\"typedef\"$condition>\n<title>$symbol</title>\n";
-    my $synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link 
linkend=\"$id\">$symbol</link></entry></row>\n";
+def OutputTypedef(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+    desc = "<refsect2 id=\"%s\" role=\"typedef\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    synop = "<row><entry role=\"typedef_keyword\">typedef</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (sid, symbol)
 
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
 
-    if (!defined ($DeclarationConditional{$symbol})) {
-        my $decl_out = &CreateValidSGML ($declaration);
-        $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
-    }
+    if  symbol in DeclarationConditional:
+        decl_out = CreateValidSGML(declaration)
+        desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
 
-    $desc .= &MakeDeprecationNote($symbol);
 
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
 
 
 #############################################################################
@@ -1573,223 +1466,204 @@ sub OutputTypedef {
 #                $declaration - the declaration of the struct.
 #############################################################################
 
-sub OutputStruct {
-    my ($symbol, $declaration) = @_;
+def OutputStruct(symbol, declaration):
+
+    is_gtype = False
+    default_to_public = True
+    if CheckIsObject(symbol):
+        logging.info("Found struct gtype: %s", symbol)
+        is_gtype = True
+        default_to_public = ObjectRoots[symbol] == 'GBoxed'
 
-    my $is_gtype = 0;
-    my $default_to_public = 1;
-    if (&CheckIsObject ($symbol)) {
-        @TRACE@("Found struct gtype: $symbol\n");
-        $is_gtype = 1;
-        $default_to_public = $ObjectRoots{$symbol} eq 'GBoxed';
-    }
 
-    my $id;
-    my $condition;
-    if ($is_gtype) {
-        $id = &CreateValidSGMLID ($symbol . "_struct");
-        $condition = &MakeConditionDescription ($symbol . "_struct");
-    } else {
-        $id = &CreateValidSGMLID ($symbol);
-        $condition = &MakeConditionDescription ($symbol);
-    }
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_struct")
+        condition = MakeConditionDescription(symbol + "_struct")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
+
 
     # Determine if it is a simple struct or it also has a typedef.
-    my $has_typedef = 0;
-    if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
-      $has_typedef = 1;
-    }
-
-    my $type_output;
-    my $desc;
-    if ($has_typedef) {
+    has_typedef = False
+    if StructHasTypedef[symbol] or re.search(r'^\s*typedef\s+', declaration):
+        has_typedef = True
+
+    type_output = None
+    desc = None
+    if has_typedef:
         # For structs with typedefs we just output the struct name.
-        $type_output = "";
-        $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>$symbol</title>\n";
-    } else {
-        $type_output = "struct";
-        $desc = "<refsect2 id=\"$id\" role=\"struct\"$condition>\n<title>struct $symbol</title>\n";
-    }
-    my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry 
role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
-
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
+        type_output = ''
+        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    else:
+        type_output = "struct"
+        desc = "<refsect2 id=\"%s\" role=\"struct\"%s>\n<title>struct %s</title>\n" % (sid, condition, 
symbol)
+
+    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (type_output, sid, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
 
     # Form a pretty-printed, private-data-removed form of the declaration
 
-    my $decl_out = "";
-    if ($declaration =~ m/^\s*$/) {
-        @TRACE@("Found opaque struct: $symbol\n");
-        $decl_out = "typedef struct _$symbol $symbol;";
-    } elsif ($declaration =~ m/^\s*struct\s+\w+\s*;\s*$/) {
-        @TRACE@("Found opaque struct: $symbol\n");
-        $decl_out = "struct $symbol;";
-    } else {
-        my $public = $default_to_public;
-        my $new_declaration = "";
-        my $decl_line;
-        my $decl = $declaration;
-
-        if ($decl =~ m/^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$/s) {
-            my $struct_contents = $2;
-
-            foreach $decl_line (split (/\n/, $struct_contents)) {
-                @TRACE@("Struct line: $decl_line\n");
-                if ($decl_line =~ m%/\*\s*<\s*public\s*>\s*\*/%) {
-                    $public = 1;
-                } elsif ($decl_line =~ m%/\*\s*<\s*(private|protected)\s*>\s*\*/%) {
-                    $public = 0;
-                } elsif ($public) {
-                    $new_declaration .= $decl_line . "\n";
-                }
-            }
-
-            if ($new_declaration) {
+    decl_out = ''
+    if re.search(r'^\s*$', declaration):
+        logging.info("Found opaque struct: %s", symbol)
+        decl_out = "typedef struct _%s %s;" % (symbol, symbol)
+    elif re.search(r'^\s*struct\s+\w+\s*;\s*', declaration):
+        logging.info("Found opaque struct: %s", symbol)
+        decl_out = "struct %s;" % symbol
+    else:
+        public = default_to_public
+        new_declaration = ''
+        decl_line = None
+        decl = declaration
+
+        m = re.search(r'^\s*(typedef\s+)?struct\s*\w*\s*(?:\/\*.*\*\/)?\s*{(.*)}\s*\w*\s*;\s*$', decl, 
flags=re.S)
+        if m:
+            struct_contents = m.group(2)
+
+            for decl_line  in struct_contents.split('\n'):
+                logging.info("Struct line: %s", decl_line)
+                m2 = re.search(r'/\*\s*<\s*public\s*>\s*\*/', decl_line)
+                m3 = re.search(r'/\*\s*<\s*(private|protected)\s*>\s*\*/', decl_line)
+                if m2:
+                    public = True
+                elif m3:
+                    public = False
+                elif public:
+                    new_declaration += decl_line + "\n"
+
+            if new_declaration:
                 # Strip any blank lines off the ends.
-                $new_declaration =~ s/^\s*\n//;
-                $new_declaration =~ s/\n\s*$/\n/;
-
-                if ($has_typedef) {
-                    $decl_out = "typedef struct {\n" . $new_declaration
-                      . "} $symbol;\n";
-                } else {
-                    $decl_out = "struct $symbol {\n" . $new_declaration
-                      . "};\n";
-                }
-            }
-        } else {
-            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                "Couldn't parse struct:\n$declaration");
-        }
+                new_declaration = '\n'.join([x.strip() for x in new_declaration.split('\n')])
+
+                if has_typedef:
+                    decl_out = "typedef struct {\n" + new_declaration + "} $symbol;\n"
+                else:
+                    decl_out = "struct %s {\n%s };\n" % (symbol, new_declaration)
+
+        else:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Couldn't parse struct:\n%s" % declaration)
 
         # If we couldn't parse the struct or it was all private, output an
         # empty struct declaration.
-        if ($decl_out eq "") {
-            if ($has_typedef) {
-                $decl_out = "typedef struct _$symbol $symbol;";
-            } else {
-                $decl_out = "struct $symbol;";
-            }
-        }
-    }
+        if decl_out == '':
+            if has_typedef:
+                decl_out = "typedef struct _%s %s;" % (symbol, symbol)
+            else:
+                decl_out = "struct %s;" % symbol
+
+    decl_out = CreateValidSGML(decl_out)
+    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
 
-    $decl_out = &CreateValidSGML ($decl_out);
-    $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
+    desc += MakeDeprecationNote(symbol)
 
-    $desc .= &MakeDeprecationNote($symbol);
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
 
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
 
     # Create a table of fields and descriptions
 
     # FIXME: Inserting &#160's into the produced type declarations here would
     #        improve the output in most situations ... except for function
     #        members of structs!
-    my @fields = ParseStructDeclaration($declaration, !$default_to_public,
-                                        0, \&MakeXRef,
-                                        sub {
-                                            "<structfield 
id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
-                                        });
-    my $params = $SymbolParams{$symbol};
+    def depfunc(*args):
+        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
+
+    fields = common.ParseStructDeclaration(declaration, not default_to_public,
+                                           0, MakeXRef, depfunc)
+    params = SymbolParams[symbol]
 
     # If no parameters are filled in, we don't generate the description
     # table, for backwards compatibility.
 
-    my $found = 0;
-    if (defined $params) {
-        for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
-            if ($params->[$i] =~ /\S/) {
-                $found = 1;
-                last;
-            }
-        }
-    }
-
-    if ($found) {
-        my %field_descrs = @$params;
-        my $missing_parameters = "";
-        my $unused_parameters = "";
-        my $id = &CreateValidSGMLID ("$symbol".".members");
-
-        $desc .= <<EOF;
-<refsect3 id="$id" role="struct_members">\n<title>Members</title>
+    found = False
+    if params:
+        for i in range(1, len(params)+1, PARAM_FIELD_COUNT):
+            if re.search(r'\S', params[i]):
+                found = True
+                break
+
+    if found:
+        field_descrs = {} # FIXME, don't know what this does, original was: @$params
+        missing_parameters = ''
+        unused_parameters = ''
+        sid = common.CreateValidSGMLID(symbol + ".members")
+
+        desc += '''<refsect3 id="%s" role="struct_members">\n<title>Members</title>
 <informaltable role="struct_members_table" pgwide="1" frame="none">
 <tgroup cols="3">
 <colspec colname="struct_members_name" colwidth="300px"/>
 <colspec colname="struct_members_description"/>
 <colspec colname="struct_members_annotations" colwidth="200px"/>
 <tbody>
-EOF
-
-        while (@fields) {
-            my $field_name = shift @fields;
-            my $text = shift @fields;
-            my $field_descr = $field_descrs{$field_name};
-            my $param_annotations = "";
-
-            $desc .= "<row role=\"member\"><entry role=\"struct_member_name\"><para>$text</para></entry>\n";
-            if (defined $field_descr) {
-                ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
-                $field_descr = &ConvertMarkDown($symbol, $field_descr);
+''' % sid
+
+        while len(fields) > 0:
+            field_name = fields.pop(0)
+            text = fields.pop(0)
+            field_descr = field_descrs[field_name]
+            param_annotations = ''
+
+            desc += "<row role=\"member\"><entry role=\"struct_member_name\"><para>%s</para></entry>\n" % 
text
+            if field_descr:
+                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descr)
+                field_descr = ConvertMarkDown(symbol, field_descr)
                 # trim
-                $field_descr =~ s/^(\s|\n)+//msg;
-                $field_descr =~ s/(\s|\n)+$//msg;
-                $desc .= "<entry role=\"struct_member_description\">$field_descr</entry>\n<entry 
role=\"struct_member_annotations\">$param_annotations</entry>\n";
-                delete $field_descrs{$field_name};
-            } else {
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Field description for $symbol"."::"."$field_name is missing in source code comment 
block.");
-                if ($missing_parameters ne "") {
-                  $missing_parameters .= ", ".$field_name;
-                } else {
-                    $missing_parameters = $field_name;
-                }
-                $desc .= "<entry /><entry />\n";
-            }
-            $desc .= "</row>\n";
-        }
-        $desc .= "</tbody></tgroup></informaltable>\n</refsect3>\n";
-        foreach my $field_name (keys %field_descrs) {
+                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M|re.S)
+                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M|re.S)
+                desc += "<entry role=\"struct_member_description\">%s</entry>\n<entry 
role=\"struct_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
+                del field_descrs[field_name]
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+
+                desc += "<entry /><entry />\n"
+
+            desc += "</row>\n"
+
+        desc += "</tbody></tgroup></informaltable>\n</refsect3>\n"
+        for field_name in field_descrs:
             # Documenting those standard fields is not required anymore, but
             # we don't want to warn if they are documented anyway.
-            if ($field_name =~ /(g_iface|parent_instance|parent_class)/) {
-                next;
-            }
-            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                "Field description for $symbol"."::"."$field_name is not used from source code comment 
block.");
-            if ($unused_parameters ne "") {
-              $unused_parameters .= ", ".$field_name;
-            } else {
-               $unused_parameters = $field_name;
-            }
-        }
+            m = re.search(r'(g_iface|parent_instance|parent_class)', field_name)
+            if m:
+                continue
+
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+            if unused_parameters != '':
+                unused_parameters += ", " + field_name
+            else:
+                unused_parameters = field_name
 
         # remember missing/unused parameters (needed in tmpl-free build)
-        if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
-            $AllIncompleteSymbols{$symbol}=$missing_parameters;
-        }
-        if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
-            $AllUnusedSymbols{$symbol}=$unused_parameters;
-        }
-    }
-    else {
-        if (scalar(@fields) > 0) {
-            if (! exists ($AllIncompleteSymbols{$symbol})) {
-                $AllIncompleteSymbols{$symbol}="<items>";
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Field descriptions for struct $symbol are missing in source code comment block.");
-                @TRACE@("Remaining structs fields: ".@fields.":".join(',',@fields)."\n");
-            }
-        }
-    }
-
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
+
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+    else:
+        if len(fields) > 0:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field descriptions for struct %s are missing in source code comment 
block." % symbol)
+                logging.info("Remaining structs fields: " + ':'.join(fields) + "\n")
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
 
 
 #############################################################################
@@ -1799,156 +1673,140 @@ EOF
 #                $declaration - the declaration of the union.
 #############################################################################
 
-sub OutputUnion {
-    my ($symbol, $declaration) = @_;
+def OutputUnion(symbol, declaration):
+
+    is_gtype = False
+    if CheckIsObject(symbol):
+        logging.info("Found union gtype: %s\n", symbol)
+        is_gtype = True
+
 
-    my $is_gtype = 0;
-    if (&CheckIsObject ($symbol)) {
-        @TRACE@("Found union gtype: $symbol\n");
-        $is_gtype = 1;
-    }
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_union")
+        condition = MakeConditionDescription(symbol + "_union")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
 
-    my $id;
-    my $condition;
-    if ($is_gtype) {
-        $id = &CreateValidSGMLID ($symbol . "_union");
-        $condition = &MakeConditionDescription ($symbol . "_union");
-    } else {
-        $id = &CreateValidSGMLID ($symbol);
-        $condition = &MakeConditionDescription ($symbol);
-    }
 
     # Determine if it is a simple struct or it also has a typedef.
-    my $has_typedef = 0;
-    if ($StructHasTypedef{$symbol} || $declaration =~ m/^\s*typedef\s+/) {
-      $has_typedef = 1;
-    }
-
-    my $type_output;
-    my $desc;
-    if ($has_typedef) {
+    has_typedef = False
+    if StructHasTypedef[symbol] or re.search(r'^\s*typedef\s+', declaration):
+        has_typedef = True
+
+
+    type_output = None
+    desc = None
+    if has_typedef:
         # For unions with typedefs we just output the union name.
-        $type_output = "";
-        $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>$symbol</title>\n";
-    } else {
-        $type_output = "union";
-        $desc = "<refsect2 id=\"$id\" role=\"union\"$condition>\n<title>union $symbol</title>\n";
-    }
-    my $synop = "<row><entry role=\"datatype_keyword\">${type_output}</entry><entry 
role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
-
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
-    $desc .= &MakeDeprecationNote($symbol);
-
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
+        type_output = ''
+        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+    else:
+        type_output = "union"
+        desc = "<refsect2 id=\"%s\" role=\"union\"%s>\n<title>union %s</title>\n" % (sid, condition, symbol)
+
+    synop = "<row><entry role=\"datatype_keyword\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s$id\">%s</link></entry></row>\n" % (type_output, sid, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
 
     # Create a table of fields and descriptions
 
     # FIXME: Inserting &#160's into the produced type declarations here would
     #        improve the output in most situations ... except for function
     #        members of structs!
-    my @fields = ParseStructDeclaration($declaration, 0,
-                                        0, \&MakeXRef,
-                                        sub {
-                                            "<structfield 
id=\"".&CreateValidSGMLID("$id.$_[0]")."\">$_[0]</structfield>";
-                                        });
-    my $params = $SymbolParams{$symbol};
+    def pfunc(*args):
+        return '<structfield id="%s">%s</structfield>' % (common.CreateValidSGMLID(sid + '.' + args[0]), 
args[0])
+    fields = common.ParseStructDeclaration(declaration, 0, 0, MakeXRef, pfunc)
+    params = SymbolParams[symbol]
 
     # If no parameters are filled in, we don't generate the description
     # table, for backwards compatibility
 
-    my $found = 0;
-    if (defined $params) {
-        for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
-            if ($params->[$i] =~ /\S/) {
-                $found = 1;
-                last;
-            }
-        }
-    }
-
-    if ($found) {
-        my %field_descrs = @$params;
-        my $missing_parameters = "";
-        my $unused_parameters = "";
-        my $id = &CreateValidSGMLID ("$symbol".".members");
-
-        $desc .= <<EOF;
-<refsect3 id="$id" role="union_members">\n<title>Members</title>
+    found = False
+    if params:
+        for i in range(1, len(params)+1, PARAM_FIELD_COUNT):
+            if re.search(r'\S', params[i]):
+                found = True
+                break
+
+    if found:
+        field_descrs = {} # FIXME same as above: @$params
+        missing_parameters = ''
+        unused_parameters = ''
+        sid = common.CreateValidSGMLID('%s.members' % symbol)
+
+        desc += '''<refsect3 id="%s" role="union_members">\n<title>Members</title>
 <informaltable role="union_members_table" pgwide="1" frame="none">
 <tgroup cols="3">
 <colspec colname="union_members_name" colwidth="300px"/>
 <colspec colname="union_members_description"/>
 <colspec colname="union_members_annotations" colwidth="200px"/>
 <tbody>
-EOF
+''' % sid
 
-        while (@fields) {
-            my $field_name = shift @fields;
-            my $text = shift @fields;
-            my $field_descr = $field_descrs{$field_name};
-            my $param_annotations = "";
+        while len(fields) > 0:
+            field_name = fields.pop(0)
+            text = fields.pop(0)
+            field_descr = field_descrs[field_name]
+            param_annotations = ''
 
-            $desc .= "<row><entry role=\"union_member_name\"><para>$text</para></entry>\n";
-            if (defined $field_descr) {
-                ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
-                $field_descr = &ConvertMarkDown($symbol, $field_descr);
+            desc += "<row><entry role=\"union_member_name\"><para>%s</para></entry>\n" % text
+            if field_descr:
+                (field_descr, param_annotations) = ExpandAnnotation(symbol, field_descr)
+                field_descr = ConvertMarkDown(symbol, field_descr)
 
                 # trim
-                $field_descr =~ s/^(\s|\n)+//msg;
-                $field_descr =~ s/(\s|\n)+$//msg;
-                $desc .= "<entry role=\"union_member_description\">$field_descr</entry>\n<entry 
role=\"union_member_annotations\">$param_annotations</entry>\n";
-                delete $field_descrs{$field_name};
-            } else {
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Field description for $symbol"."::"."$field_name is missing in source code comment 
block.");
-                if ($missing_parameters ne "") {
-                    $missing_parameters .= ", ".$field_name;
-                } else {
-                    $missing_parameters = $field_name;
-                }
-                $desc .= "<entry /><entry />\n";
-            }
-            $desc .= "</row>\n";
-        }
-        $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
-        foreach my $field_name (keys %field_descrs) {
-            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                "Field description for $symbol"."::"."$field_name is not used from source code comment 
block.");
-            if ($unused_parameters ne "") {
-              $unused_parameters .= ", ".$field_name;
-            } else {
-               $unused_parameters = $field_name;
-            }
-        }
+                field_descr = re.sub(r'^(\s|\n)+', '', field_descr, flags=re.M|re.S)
+                field_descr = re.sub(r'(\s|\n)+$', '', field_descr, flags=re.M|re.S)
+                desc += "<entry role=\"union_member_description\">%s</entry>\n<entry 
role=\"union_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
+                del field_descrs[field_name]
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+
+                desc += "<entry /><entry />\n"
+
+            desc += "</row>\n"
+
+        desc += "</tbody></tgroup></informaltable>\n</refsect3>"
+        for field_name in field_descrs:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Field description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+            if unused_parameters != '':
+                unused_parameters += ", " + field_name
+            else:
+                unused_parameters = field_name
 
         # remember missing/unused parameters (needed in tmpl-free build)
-        if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
-            $AllIncompleteSymbols{$symbol}=$missing_parameters;
-        }
-        if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
-            $AllUnusedSymbols{$symbol}=$unused_parameters;
-        }
-    }
-    else {
-        if (scalar(@fields) > 0) {
-            if (! exists ($AllIncompleteSymbols{$symbol})) {
-                $AllIncompleteSymbols{$symbol}="<items>";
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Field descriptions for union $symbol are missing in source code comment block.");
-                @TRACE@("Remaining union fields: ".@fields.":".join(',',@fields)."\n");
-            }
-        }
-    }
-
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
 
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+    else:
+        if len(fields) > 0:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Field descriptions for union %s are missing in source code comment 
block." % symbol)
+                logging.info("Remaining union fields: " + ':'.join(fields) + "\n")
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
 
 #############################################################################
 # Function    : OutputEnum
@@ -1957,129 +1815,112 @@ EOF
 #                $declaration - the declaration of the enum.
 #############################################################################
 
-sub OutputEnum {
-    my ($symbol, $declaration) = @_;
+def OutputEnum(symbol, declaration):
+    is_gtype = False
+    if CheckIsObject(symbol):
+        logging.info("Found enum gtype: %s", symbol)
+        is_gtype = True
 
-    my $is_gtype = 0;
-    if (&CheckIsObject ($symbol)) {
-        @TRACE@("Found enum gtype: $symbol\n");
-        $is_gtype = 1;
-    }
+    sid = None
+    condition = None
+    if is_gtype:
+        sid = common.CreateValidSGMLID(symbol + "_enum")
+        condition = MakeConditionDescription(symbol + "_enum")
+    else:
+        sid = common.CreateValidSGMLID(symbol)
+        condition = MakeConditionDescription(symbol)
 
-    my $id;
-    my $condition;
-    if ($is_gtype) {
-        $id = &CreateValidSGMLID ($symbol . "_enum");
-        $condition = &MakeConditionDescription ($symbol . "_enum");
-    } else {
-        $id = &CreateValidSGMLID ($symbol);
-        $condition = &MakeConditionDescription ($symbol);
-    }
 
-    my $synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link 
linkend=\"$id\">$symbol</link></entry></row>\n";
-    my $desc = "<refsect2 id=\"$id\" role=\"enum\"$condition>\n<title>enum $symbol</title>\n";
+    synop = "<row><entry role=\"datatype_keyword\">enum</entry><entry role=\"function_name\"><link 
linkend=\"\">$symbol</link></entry></row>\n" % sid
+    desc = "<refsect2 id=\"%s\" role=\"enum\"%s>\n<title>enum %s</title>\n" % (sid, condition, symbol)
 
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
-    $desc .= &MakeDeprecationNote($symbol);
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+    desc += MakeDeprecationNote(symbol)
 
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
 
     # Create a table of fields and descriptions
 
-    my @fields = ParseEnumDeclaration($declaration);
-    my $params = $SymbolParams{$symbol};
+    fields = common.ParseEnumDeclaration(declaration)
+    params = SymbolParams[symbol]
 
     # If nothing at all is documented log a single summary warning at the end.
     # Otherwise, warn about each undocumented item.
 
-    my $found = 0;
-    if (defined $params) {
-        for (my $i = 1; $i <= $#$params; $i += $PARAM_FIELD_COUNT) {
-            if ($params->[$i] =~ /\S/) {
-                $found = 1;
-                last;
-            }
-        }
-    }
-
-    my %field_descrs = (defined $params ? @$params : ());
-    my $missing_parameters = "";
-    my $unused_parameters = "";
-
-    $id = &CreateValidSGMLID ("$symbol".".members");
-    $desc .= <<EOF;
-<refsect3 id="$id" role="enum_members">\n<title>Members</title>
+    found = False
+    if params:
+        for i in range(1, len(params), PARAM_FIELD_COUNT):
+            if re.search(r'\S', params[i]):
+                found = True
+                break
+
+    field_descrs = params if params else {}
+    missing_parameters = ''
+    unused_parameters = ''
+
+    sid = common.CreateValidSGMLID("%s.members" % symbol)
+    desc += '''<refsect3 id="%s" role="enum_members">\n<title>Members</title>
 <informaltable role="enum_members_table" pgwide="1" frame="none">
 <tgroup cols="3">
 <colspec colname="enum_members_name" colwidth="300px"/>
 <colspec colname="enum_members_description"/>
 <colspec colname="enum_members_annotations" colwidth="200px"/>
 <tbody>
-EOF
-
-    for my $field_name (@fields) {
-        my $field_descr = $field_descrs{$field_name};
-        my $param_annotations = "";
-
-        $id = &CreateValidSGMLID ($field_name);
-        $condition = &MakeConditionDescription ($field_name);
-        $desc .= "<row role=\"constant\"><entry role=\"enum_member_name\"><para 
id=\"$id\">$field_name</para></entry>\n";
-        if (defined $field_descr) {
-            ($field_descr,$param_annotations) = &ExpandAnnotation($symbol, $field_descr);
-            $field_descr = &ConvertMarkDown($symbol, $field_descr);
-            $desc .= "<entry role=\"enum_member_description\">$field_descr</entry>\n<entry 
role=\"enum_member_annotations\">$param_annotations</entry>\n";
-            delete $field_descrs{$field_name};
-        } else {
-            if ($found) {
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Value description for $symbol"."::"."$field_name is missing in source code comment 
block.");
-                if ($missing_parameters ne "") {
-                    $missing_parameters .= ", ".$field_name;
-                } else {
-                    $missing_parameters = $field_name;
-                }
-            }
-            $desc .= "<entry /><entry />\n";
-        }
-        $desc .= "</row>\n";
-    }
-    $desc .= "</tbody></tgroup></informaltable>\n</refsect3>";
-    foreach my $field_name (keys %field_descrs) {
-        &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-            "Value description for $symbol"."::"."$field_name is not used from source code comment block.");
-        if ($unused_parameters ne "") {
-            $unused_parameters .= ", ".$field_name;
-        } else {
-            $unused_parameters = $field_name;
-        }
-    }
+''' % sid
+
+    for field_name in fields:
+        field_descr = field_descrs[field_name]
+        param_annotations = ''
+
+        sid = common.CreateValidSGMLID(field_name)
+        condition = MakeConditionDescription(field_name)
+        desc += "<row role=\"constant\"><entry role=\"enum_member_name\"><para 
id=\"%s\">$field_name</para></entry>\n" % sid
+        if field_descr:
+            field_descr, param_annotations = ExpandAnnotation(symbol, field_descr)
+            field_descr = ConvertMarkDown(symbol, field_descr)
+            desc += "<entry role=\"enum_member_description\">%s</entry>\n<entry 
role=\"enum_member_annotations\">%s</entry>\n" % (field_descr, param_annotations)
+            del field_descrs[field_name]
+        else:
+            if found:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Value description for %s::%s is missing in source code comment block." % 
(symbol, field_name))
+                if missing_parameters != '':
+                    missing_parameters += ", " + field_name
+                else:
+                    missing_parameters = field_name
+            desc += "<entry /><entry />\n"
+        desc += "</row>\n"
+
+    desc += "</tbody></tgroup></informaltable>\n</refsect3>"
+    for field_name in field_descrs:
+        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                          "Value description for %s::%s is not used from source code comment block." % 
(symbol, field_name))
+        if unused_parameters != '':
+            unused_parameters += ", " + field_name
+        else:
+            unused_parameters = field_name
 
     # remember missing/unused parameters (needed in tmpl-free build)
-    if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
-        $AllIncompleteSymbols{$symbol}=$missing_parameters;
-    }
-    if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
-        $AllUnusedSymbols{$symbol}=$unused_parameters;
-    }
-
-    if (!$found) {
-        if (scalar(@fields) > 0) {
-            if (! exists ($AllIncompleteSymbols{$symbol})) {
-                $AllIncompleteSymbols{$symbol}="<items>";
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "Value descriptions for $symbol are missing in source code comment block.");
-            }
-        }
-    }
-
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+    if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+        AllIncompleteSymbols[symbol] = missing_parameters
+
+    if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+        AllUnusedSymbols[symbol] = unused_parameters
+
+    if not found:
+        if len(fields) > 0:
+            if symbol not in AllIncompleteSymbols:
+                AllIncompleteSymbols[symbol] = "<items>"
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "Value descriptions for %s are missing in source code comment block." % 
symbol)
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
 
 
 #############################################################################
@@ -2089,58 +1930,69 @@ EOF
 #                $declaration - the declaration of the variable.
 #############################################################################
 
-sub OutputVariable {
-    my ($symbol, $declaration) = @_;
-    my $id = &CreateValidSGMLID ($symbol);
-    my $condition = &MakeConditionDescription ($symbol);
-
-    @TRACE@("ouputing variable: '$symbol' '$declaration'");
-
-    my $type_output;
-    if ($declaration =~ 
m/^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;/)
 {
-        my $mod1 = defined ($1) ? $1 : "";
-        my $ptr = defined ($3) ? $3 : "";
-        my $space = defined ($4) ? $4 : "";
-        my $mod2 = defined ($5) ? $5 : "";
-        $type_output = "extern $mod1$ptr$space$mod2";
-    } elsif ($declaration =~ 
m/^\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=/)
 {
-        my $mod1 = defined ($1) ? $1 : "";
-        my $ptr = defined ($3) ? $3 : "";
-        my $space = defined ($4) ? $4 : "";
-        my $mod2 = defined ($5) ? $5 : "";
-        $type_output = "$mod1$ptr$space$mod2";
-    } else {
-        $type_output = "extern";
-    }
-    my $synop = "<row><entry role=\"variable_type\">${type_output}</entry><entry 
role=\"function_name\"><link linkend=\"$id\">$symbol</link></entry></row>\n";
-
-    my $desc = "<refsect2 id=\"$id\" role=\"variable\"$condition>\n<title>$symbol</title>\n";
-
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
-
-    my $decl_out = &CreateValidSGML ($declaration);
-    $desc .= "<programlisting language=\"C\">$decl_out</programlisting>\n";
-
-    $desc .= &MakeDeprecationNote($symbol);
-
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
-    if (defined ($SymbolAnnotations{$symbol})) {
-        my $param_desc = $SymbolAnnotations{$symbol};
-        my $param_annotations = "";
-        ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
-        if ($param_annotations ne "") {
-            $desc .= "\n<para>$param_annotations</para>";
-        }
-    }
-
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+def OutputVariable(symbol, declaration):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
+
+    logging.info("ouputing variable: '%s' '%s'", symbol, declaration)
+
+    type_output = None
+    m1 = 
re.search(r'^\s*extern\s+((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*;',
 declaration)
+    m2 = 
re.search(r'\s*((const\s+|signed\s+|unsigned\s+|long\s+|short\s+)*\w+)(\s+\*+|\*+|\s)(\s*)(const\s+)*([A-Za-z]\w*)\s*=',
 declaration)
+    if m1:
+        g1 = m1.group(1)
+        g2 = m1.group(2)
+        g3 = m1.group(3)
+        g4 = m1.group(4)
+        g5 = m1.group(5)
+        mod1 = g1 if g1 else ''
+        ptr = g3 if g2 else ''
+        space = g4 if g4 else ''
+        mod2 = g5 if g5 else ''
+        type_output = "extern %s%s%s%s" % (mod1, ptr, space, mod2)
+    elif m2:
+        g1 = m1.group(1)
+        g2 = m1.group(2)
+        g3 = m1.group(3)
+        g4 = m1.group(4)
+        g5 = m1.group(5)
+        mod1 = g1 if g1 else ''
+        ptr = g3 if g3 else ''
+        space = g4 if g4 else ''
+        mod2 = g5 if g5 else ''
+        type_output = '%s%s%s%s' % (mod1, ptr, space, mod2)
+    else:
+        type_output = "extern"
+
+    synop = "<row><entry role=\"variable_type\">%s</entry><entry role=\"function_name\"><link 
linkend=\"%s\">%s</link></entry></row>\n" % (type_output, sid, symbol)
+
+    desc = "<refsect2 id=\"%s\" role=\"variable\"%s>\n<title>%s</title>\n" % (sid, condition, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    decl_out = CreateValidSGML(declaration)
+    desc += "<programlisting language=\"C\">%s</programlisting>\n" % decl_out
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    if symbol in SymbolAnnotations:
+        param_desc = SymbolAnnotations[symbol]
+        param_annotations = ''
+        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+        if param_annotations != '':
+            desc += "\n<para>$param_annotations</para>"
+
+
+
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
+
 
 
 #############################################################################
@@ -2150,102 +2002,88 @@ sub OutputVariable {
 #                $declaration - the declaration of the function.
 #############################################################################
 
-sub OutputFunction {
-    my ($symbol, $declaration, $symbol_type) = @_;
-    my $id = &CreateValidSGMLID ($symbol);
-    my $condition = &MakeConditionDescription ($symbol);
+def OutputFunction(symbol, declaration, symbol_type):
+    sid = common.CreateValidSGMLID(symbol)
+    condition = MakeConditionDescription(symbol)
 
-    # Take out the return type     $1                                                                        
               $2   $3
-    $declaration =~ 
s/<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n//;
-    my $type_modifier = defined($1) ? $1 : "";
-    my $type = $2;
-    my $pointer = $3;
+    # Take out the return type          $1                                                                   
                    $2   $3
+    m = 
re.search(r'<RETURNS>\s*((?:const\s+|G_CONST_RETURN\s+|signed\s+|unsigned\s+|long\s+|short\s+|struct\s+|enum\s+)*)(\w+)(\s*\**\s*(?:const|G_CONST_RETURN)?\s*\**\s*(?:restrict)?\s*)<\/RETURNS>\n',
 declaration)
+    type_modifier = m.group(1) if m.group(1) else ''
+    type = m.group(2)
+    pointer = m.group(3)
     # Trim trailing spaces as we are going to pad to $RETURN_TYPE_FIELD_WIDTH below anyway
-    $pointer =~ s/\s+$//;
-    my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
-    my $start = "";
-    #if ($symbol_type eq 'USER_FUNCTION') {
-    #    $start = "typedef ";
-    #}
+    pointer = pointer.rstrip()
+    xref = MakeXRef(type, tagify(type, "returnvalue"))
+    start = ''
+    #if ($symbol_type == 'USER_FUNCTION')
+    #    $start = "typedef "
+    #
 
     # We output const rather than G_CONST_RETURN.
-    $type_modifier =~ s/G_CONST_RETURN/const/g;
-    $pointer =~ s/G_CONST_RETURN/const/g;
-    $pointer =~ s/^\s+/&#160;/g;
-
-    my $ret_type_output;
-    $ret_type_output = "$start$type_modifier$xref$pointer\n";
-
-    my $indent_len;
-    $indent_len = length ($symbol) + 2;
-    my $char1 = my $char2 = my $char3 = "";
-    if ($symbol_type eq 'USER_FUNCTION') {
-        $indent_len += 3;
-        $char1 = "<phrase role=\"c_punctuation\">(</phrase>";
-        $char2 = "*";
-        $char3 = "<phrase role=\"c_punctuation\">)</phrase>";
-    }
-
-    my ($symbol_output, $symbol_desc_output);
-    $symbol_output = "$char1<link linkend=\"$id\">$char2$symbol</link>$char3";
-    if ($indent_len < $MAX_SYMBOL_FIELD_WIDTH) {
-        $symbol_desc_output = "$char1$char2$symbol$char3 ";
-    } else {
-        $indent_len = $MAX_SYMBOL_FIELD_WIDTH - 8;
-        $symbol_desc_output = "$char1$char2$symbol$char3\n"
-          . (' ' x ($indent_len - 1));
-    }
-
-    my $synop = "<row><entry role=\"function_type\">${ret_type_output}</entry><entry 
role=\"function_name\">${symbol_output}&#160;<phrase role=\"c_punctuation\">()</phrase></entry></row>\n";
-
-    my $desc = "<refsect2 id=\"$id\" role=\"function\"$condition>\n<title>${symbol}&#160;()</title>\n";
-
-    $desc .= MakeIndexterms($symbol, $id);
-    $desc .= "\n";
-    $desc .= OutputSymbolExtraLinks($symbol);
-
-    $desc  .= "<programlisting language=\"C\">${ret_type_output}$symbol_desc_output(";
-
-    my @fields = ParseFunctionDeclaration($declaration, \&MakeXRef,
-                                        sub {
-                                            &tagify($_[0],"parameter");
-                                        });
-
-    for (my $i = 1; $i <= $#fields; $i += 2) {
-        my $field_name = $fields[$i];
-
-        if ($i == 1) {
-            $desc  .= "$field_name";
-        } else {
-            $desc  .= ",\n"
-                . (' ' x $indent_len)
-                . "$field_name";
-        }
-
-    }
-
-    $desc  .= ");</programlisting>\n";
-
-    $desc .= &MakeDeprecationNote($symbol);
-
-    if (defined ($SymbolDocs{$symbol})) {
-        $desc .= &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-    }
-    if (defined ($SymbolAnnotations{$symbol})) {
-        my $param_desc = $SymbolAnnotations{$symbol};
-        my $param_annotations = "";
-        ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
-        if ($param_annotations ne "") {
-            $desc .= "\n<para>$param_annotations</para>";
-        }
-    }
-
-    $desc .= &OutputParamDescriptions ("FUNCTION", $symbol, @fields);
-    $desc .= OutputSymbolTraits ($symbol);
-    $desc .= "</refsect2>\n";
-    return ($synop, $desc);
-}
+    type_modifier = re.sub(r'G_CONST_RETURN', 'const', type_modifier)
+    pointer = re.sub(r'G_CONST_RETURN', 'const', pointer)
+    pointer = re.sub(r'^\s+', '&#160', pointer)
+
+    ret_type_output = None
+    ret_type_output = "%s%s%s%s\n" % (start, type_modifier, xref, pointer)
+
+    indent_len = len(symbol) + 2
+    char1 = char2 = char3 = ''
+    if symbol_type == 'USER_FUNCTION':
+        indent_len += 3
+        char1 = "<phrase role=\"c_punctuation\">(</phrase>"
+        char2 = "*"
+        char3 = "<phrase role=\"c_punctuation\">)</phrase>"
+
+
+    symbol_output = "%s<link linkend=\"%s\">%s%s</link>%s" % (char1, sid, char2, symbol, char3)
+    if indent_len < MAX_SYMBOL_FIELD_WIDTH:
+        symbol_desc_output = "%s%s%s%s" % (char1, char2, symbol, char3)
+    else:
+        indent_len = MAX_SYMBOL_FIELD_WIDTH - 8
+        symbol_desc_output = ('%s%s%s%s\n' % (char1, char2, symbol, char3)) + (' ' * (indent_len - 1))
+
+    synop = "<row><entry role=\"function_type\">%s</entry><entry role=\"function_name\">%s&#160;<phrase 
role=\"c_punctuation\">()</phrase></entry></row>\n" % (ret_type_output, symbol_output)
+
+    desc = "<refsect2 id=\"%s\" role=\"function\"%s>\n<title>%s&#160;()</title>\n" % (sid, condition, symbol)
+
+    desc += MakeIndexterms(symbol, sid)
+    desc += "\n"
+    desc += OutputSymbolExtraLinks(symbol)
+
+    desc += "<programlisting language=\"C\">%s%s(" % (ret_type_output, symbol_desc_output)
+
+    def tagfun(*args):
+        return tagify(args[0], "parameter")
 
+    fields = common.ParseFunctionDeclaration(declaration, MakeXRef, tagfun)
+
+    for i in range(1, len(fields) +1, 2):
+        field_name = fields[i]
+
+        if i == 1:
+            desc += field_name
+        else:
+            desc += ",\n" + (' ' * indent_len) + field_name
+
+    desc += ");</programlisting>\n"
+
+    desc += MakeDeprecationNote(symbol)
+
+    if symbol in SymbolDocs:
+        desc += ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+    if symbol in SymbolAnnotations:
+        param_desc = SymbolAnnotations[symbol]
+        param_annotations = ''
+        (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+        if param_annotations != '':
+            desc += "\n<para>%s</para>" % param_annotations
+
+    desc += OutputParamDescriptions("FUNCTION", symbol, fields)
+    desc += OutputSymbolTraits(symbol)
+    desc += "</refsect2>\n"
+    return (synop, desc)
 
 #############################################################################
 # Function    : OutputParamDescriptions
@@ -2258,123 +2096,110 @@ sub OutputFunction {
 #                  undocumented/unused entries
 #############################################################################
 
-sub OutputParamDescriptions {
-    my ($symbol_type, $symbol, @fields) = @_;
-    my $output = "";
-    my $params = $SymbolParams{$symbol};
-    my $num_params = 0;
-    my %field_descrs = ();
-
-    if (@fields) {
-        %field_descrs = @fields;
-        delete $field_descrs{"void"};
-        delete $field_descrs{"Returns"};
-    }
-
-    if (defined $params) {
-        my $returns = "";
-        my $params_desc = "";
-        my $missing_parameters = "";
-        my $unused_parameters = "";
-        my $j;
-
-        for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
-            my $param_name = $$params[$j];
-            my $param_desc = $$params[$j + 1];
-            my $param_annotations = "";
-
-            ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
-            $param_desc = &ConvertMarkDown($symbol, $param_desc);
+def OutputParamDescriptions(symbol_type, symbol, fields):
+    output = ''
+    params = SymbolParams[symbol]
+    num_params = 0
+    field_descrs = {}
+
+    if len(fields) > 0:
+        field_descrs = {} # FIXME convert @fields
+        del field_descrs["void"]
+        del field_descrs["Returns"]
+
+
+    if params:
+        returns = ''
+        params_desc = ''
+        missing_parameters = ''
+        unused_parameters = ''
+
+        for j in range(0, len(params), PARAM_FIELD_COUNT):
+            param_name = params[j]
+            param_desc = params[j + 1]
+            param_annotations = ''
+
+            (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+            param_desc = ConvertMarkDown(symbol, param_desc)
             # trim
-            $param_desc =~ s/^(\s|\n)+//msg;
-            $param_desc =~ s/(\s|\n)+$//msg;
-            if ($param_name eq "Returns") {
-                $returns = $param_desc;
-                if ($param_annotations ne "") {
-                    $returns .= "\n<para>$param_annotations</para>";
-                }
-            } elsif ($param_name eq "void") {
-                # FIXME: &LogWarning()?
-                @TRACE@("!!!! void in params for $symbol?\n");
-            } else {
-                if (@fields) {
-                    if (!defined $field_descrs{$param_name}) {
-                        &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                            "Parameter description for $symbol"."::"."$param_name is not used from source 
code comment block.");
-                        if ($unused_parameters ne "") {
-                          $unused_parameters .= ", ".$param_name;
-                        } else {
-                           $unused_parameters = $param_name;
-                        }
-                    } else {
-                        delete $field_descrs{$param_name};
-                    }
-                }
-                if($param_desc ne "") {
-                    $params_desc .= "<row><entry 
role=\"parameter_name\"><para>$param_name</para></entry>\n<entry 
role=\"parameter_description\">$param_desc</entry>\n<entry 
role=\"parameter_annotations\">$param_annotations</entry></row>\n";
-                    $num_params++;
-                }
-            }
-        }
-        foreach my $param_name (keys %field_descrs) {
-            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                "Parameter description for $symbol"."::"."$param_name is missing in source code comment 
block.");
-            if ($missing_parameters ne "") {
-              $missing_parameters .= ", ".$param_name;
-            } else {
-               $missing_parameters = $param_name;
-            }
-        }
+            param_desc = re.sub(r'^(\s|\n)+', '', param_desc, flags=re.M|re.S)
+            param_desc = re.sub(r'(\s|\n)+', '', param_desc, flags=re.M|re.S)
+            if param_name == "Returns":
+                returns = param_desc
+                if param_annotations != '':
+                    returns += "\n<para>%s</para>" % param_annotations
+
+                elif param_name == "void":
+                    # FIXME: &common.LogWarning()?
+                    logging.info("!!!! void in params for %s?\n", symbol)
+            else:
+                if len(fields) > 0:
+                    if param_name in field_descrs:
+                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                          "Parameter description for %s::%s is not used from source code 
comment block." % (symbol, param_name))
+                        if unused_parameters != '':
+                            unused_parameters += ", " + param_name
+                        else:
+                            unused_parameters = param_name
+                    else:
+                        del field_descrs[param_name]
+
+
+                if param_desc != '':
+                    params_desc += "<row><entry role=\"parameter_name\"><para>%s</para></entry>\n<entry 
role=\"parameter_description\">%s</entry>\n<entry role=\"parameter_annotations\">%s</entry></row>\n" % 
(param_name, param_desc, param_annotations)
+                    num_params += 1
+
+        for param_name in field_descrs:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Parameter description for %s::%s is missing in source code comment block." % 
(symbol, param_name))
+            if missing_parameters != '':
+                missing_parameters += ", " + param_name
+            else:
+                missing_parameters = param_name
 
         # Signals have an implicit user_data parameter which we describe.
-        if ($symbol_type eq "SIGNAL") {
-            $params_desc .= "<row><entry 
role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry 
role=\"parameter_description\"><simpara>user data set when the signal handler was 
connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n";
-        }
+        if symbol_type == "SIGNAL":
+            params_desc += "<row><entry role=\"parameter_name\"><simpara>user_data</simpara></entry>\n<entry 
role=\"parameter_description\"><simpara>user data set when the signal handler was 
connected.</simpara></entry>\n<entry role=\"parameter_annotations\"></entry></row>\n"
+
 
         # Start a table if we need one.
-        if ($params_desc ne "") {
-          my $id = &CreateValidSGMLID ("$symbol".".parameters");
+        if params_desc != '':
+            sid = common.CreateValidSGMLID("%s.parameters" % symbol)
 
-          $output .= <<EOF;
-<refsect3 id="$id" role="parameters">\n<title>Parameters</title>
+            output += '''<refsect3 id="%s" role="parameters">\n<title>Parameters</title>
 <informaltable role="parameters_table" pgwide="1" frame="none">
 <tgroup cols="3">
 <colspec colname="parameters_name" colwidth="150px"/>
 <colspec colname="parameters_description"/>
 <colspec colname="parameters_annotations" colwidth="200px"/>
 <tbody>
-EOF
-          $output .= $params_desc;
-          $output .= "</tbody></tgroup></informaltable>\n</refsect3>";
-        }
+''' % sid
+            output += params_desc
+            output += "</tbody></tgroup></informaltable>\n</refsect3>"
 
         # Output the returns info last
-        if ($returns ne "") {
-          my $id = &CreateValidSGMLID ("$symbol".".returns");
+        if returns != '':
+            sid = common.CreateValidSGMLID("%s.returns" % symbol)
+
+            output += '''
+<refsect3 id="%s" role=\"returns\">\n<title>Returns</title>
+''' % sid
+            output += returns
+            output += "\n</refsect3>"
 
-          $output .= <<EOF;
-<refsect3 id="$id" role=\"returns\">\n<title>Returns</title>
-EOF
-          $output .= $returns;
-          $output .= "\n</refsect3>";
-        }
 
         # remember missing/unused parameters (needed in tmpl-free build)
-        if (($missing_parameters ne "") and (! exists ($AllIncompleteSymbols{$symbol}))) {
-            $AllIncompleteSymbols{$symbol}=$missing_parameters;
-        }
-        if (($unused_parameters ne "") and (! exists ($AllUnusedSymbols{$symbol}))) {
-            $AllUnusedSymbols{$symbol}=$unused_parameters;
-        }
-    }
-    if (($num_params == 0) && @fields && (scalar(keys(%field_descrs)) > 0)) {
-        if (! exists ($AllIncompleteSymbols{$symbol})) {
-            $AllIncompleteSymbols{$symbol}="<parameters>";
-        }
-    }
-
-    return $output;
-}
+        if missing_parameters != '' and (symbol not in AllIncompleteSymbols):
+            AllIncompleteSymbols[symbol] = missing_parameters
+
+        if unused_parameters != '' and (symbol not in AllUnusedSymbols):
+            AllUnusedSymbols[symbol] = unused_parameters
+
+    if num_params == 0 and len(fields) > 0 and len(field_descrs) > 0:
+        if symbol not in AllIncompleteSymbols:
+            AllIncompleteSymbols[symbol] = "<parameters>"
+    return output
+
 
 
 #############################################################################
@@ -2388,23 +2213,22 @@ EOF
 # Returns     : The parsed stability level string.
 #############################################################################
 
-sub ParseStabilityLevel {
-    my ($stability, $file, $line, $message) = @_;
-
-    $stability =~ s/^\s*//;
-    $stability =~ s/\s*$//;
-    if ($stability =~ m/^stable$/i) {
-        $stability = "Stable";
-    } elsif ($stability =~ m/^unstable$/i) {
-        $stability = "Unstable";
-    } elsif ($stability =~ m/^private$/i) {
-        $stability = "Private";
-    } else {
-        &LogWarning ($file, $line, "$message is $stability.".
-            "It should be one of these: Stable, Unstable, or Private.");
-    }
-    return $stability;
-}
+def ParseStabilityLevel(stability, file, line, message):
+
+    stability = stability.strip()
+    sl = stability.lower()
+
+    if sl == 'stable':
+        stability = "Stable"
+    elif stability == 'unstable':
+        stability = "Unstable"
+    elif stability == 'private':
+        stability = "Private"
+    else:
+        common.LogWarning(file, line, "%s is %s." % (message, stability) +\
+            "It should be one of these: Stable, Unstable, or Private.")
+    return stability
+
 
 
 #############################################################################
@@ -2415,7 +2239,7 @@ sub ParseStabilityLevel {
 #                 will be overridden by the title in the template file.
 #               $section_id - the id to use for the toplevel tag.
 #               $includes - comma-separates list of include files added at top of
-#                 synopsis, with '<' '>' around them (if not already enclosed in "").
+#                 synopsis, with '<' '>' around them (if not already enclosed in '').
 #               $functions_synop - reference to the DocBook for the Functions Synopsis part.
 #               $other_synop - reference to the DocBook for the Types and Values Synopsis part.
 #               $functions_details - reference to the DocBook for the Functions Details part.
@@ -2432,159 +2256,149 @@ sub ParseStabilityLevel {
 #               $file_objects - reference to an array of objects in this file
 #############################################################################
 
-sub OutputDBFile {
-    my ($file, $title, $section_id, $includes, $functions_synop, $other_synop, $functions_details, 
$other_details, $signals_synop, $signals_desc, $args_synop, $args_desc, $hierarchy, $interfaces, 
$implementations, $prerequisites, $derived, $file_objects) = @_;
+def OutputDBFile(file, title, section_id, includes, functions_synop, other_synop, functions_details, 
other_details, signals_synop, signals_desc, args_synop, args_desc, hierarchy, interfaces, implementations, 
prerequisites, derived, file_objects):
 
-    @TRACE@("Output docbook for file $file with title '$title'\n");
+    logging.info("Output docbook for file %s with title '%s'\n", file, title)
 
     # The edited title overrides the one from the sections file.
-    my $new_title = $SymbolDocs{"$TMPL_DIR/$file:Title"};
-    if (defined ($new_title) && $new_title !~ m/^\s*$/) {
-        $title = $new_title;
-        @TRACE@("Found title: $title\n");
-    }
-    my $short_desc = $SymbolDocs{"$TMPL_DIR/$file:Short_Description"};
-    if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
-        $short_desc = "";
-    } else {
+    new_title = SymbolDocs[os.path.join(TMPL_DIR, file + ":Title")]
+    if new_title and not re.search(r'^\s*$', new_title):
+        title = new_title
+        logging.info("Found title: %s", title)
+
+    short_desc = SymbolDocs[os.path.join(TMPL_DIR, file + ":Short_Description")]
+    if not short_desc or re.search(r'^\s*$', short_desc):
+        short_desc = ''
+    else:
         # Don't use ConvertMarkDown here for now since we don't want blocks
-        $short_desc = &ExpandAbbreviations("$title:Short_description",
-                                           $short_desc);
-        @TRACE@("Found short_desc: $short_desc");
-    }
-    my $long_desc = $SymbolDocs{"$TMPL_DIR/$file:Long_Description"};
-    if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
-        $long_desc = "";
-    } else {
-        $long_desc = &ConvertMarkDown("$title:Long_description",
-                                          $long_desc);
-        @TRACE@("Found long_desc: $long_desc");
-    }
-    my $see_also = $SymbolDocs{"$TMPL_DIR/$file:See_Also"};
-    if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
-        $see_also = "";
-    } else {
-        $see_also = &ConvertMarkDown("$title:See_Also", $see_also);
-        @TRACE@("Found see_also: $see_also");
-    }
-    if ($see_also) {
-        $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See 
Also</title>\n$see_also\n</refsect1>\n";
-    }
-    my $stability = $SymbolDocs{"$TMPL_DIR/$file:Stability_Level"};
-    if (!defined ($stability) || $stability =~ m/^\s*$/) {
-        $stability = "";
-    } else {
-        $stability = &ParseStabilityLevel($stability, $file, $., "Section stability level");
-        @TRACE@("Found stability: $stability");
-    }
-    if ($stability) {
-        $AnnotationsUsed{$stability} = 1;
-        $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability 
Level</title>\n<acronym>$stability</acronym>, unless otherwise indicated\n</refsect1>\n";
-    } elsif ($DEFAULT_STABILITY) {
-        $AnnotationsUsed{$DEFAULT_STABILITY} = 1;
-        $stability = "<refsect1 id=\"$section_id.stability-level\">\n<title>Stability 
Level</title>\n<acronym>$DEFAULT_STABILITY</acronym>, unless otherwise indicated\n</refsect1>\n";
-    }
-
-    my $image = $SymbolDocs{"$TMPL_DIR/$file:Image"};
-    if (!defined ($image) || $image =~ m/^\s*$/) {
-      $image = "";
-    } else {
-      $image =~ s/^\s*//;
-      $image =~ s/\s*$//;
-
-      my $format;
-
-      if ($image =~ /jpe?g$/i) {
-        $format = "format='JPEG'";
-      } elsif ($image =~ /png$/i) {
-        $format = "format='PNG'";
-      } elsif ($image =~ /svg$/i) {
-        $format = "format='SVG'";
-      } else {
-        $format = "";
-      }
-
-      $image = "  <inlinegraphic fileref='$image' $format/>\n"
-    }
-
-    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
-        gmtime (time);
-    my $month = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$mon];
-    $year += 1900;
-
-    my $include_output = "";
-    if ($includes) {
-      $include_output .= "<refsect1 id=\"$section_id.includes\"><title>Includes</title><synopsis>";
-      my $include;
-      foreach $include (split (/,/, $includes)) {
-        if ($include =~ m/^\".+\"$/) {
-          $include_output .= "#include ${include}\n";
-        }
-        else {
-          $include =~ s/^\s+|\s+$//gs;
-          $include_output .= "#include &lt;${include}&gt;\n";
-        }
-      }
-      $include_output .= "</synopsis></refsect1>\n";
-    }
-
-    my $extralinks = OutputSectionExtraLinks($title,"Section:$file");
-
-    my $old_db_file = "$DB_OUTPUT_DIR/$file.xml";
-    my $new_db_file = "$DB_OUTPUT_DIR/$file.xml.new";
-
-    open (OUTPUT, ">$new_db_file")
-        || die "Can't create $new_db_file: $!";
-
-    my $object_anchors = "";
-    foreach my $object (@$file_objects) {
-        next if ($object eq $section_id);
-        my $id = CreateValidSGMLID($object);
-        @TRACE@("Adding anchor for $object\n");
-        $object_anchors .= "<anchor id=\"$id\"/>";
-    }
+        short_desc = ExpandAbbreviations(":Short_description" % title,
+                                         short_desc)
+        logging.info("Found short_desc: %s", short_desc)
+
+    long_desc = SymbolDocs[os.path.join(TMPL_DIR, file + ":Long_Description")]
+    if not long_desc or re.search(r'^\s*$', long_desc):
+        long_desc = ''
+    else:
+        long_desc = ConvertMarkDown(":Long_description" % title,
+                                    long_desc)
+        logging.info("Found long_desc: ", long_desc)
+
+    see_also = SymbolDocs[os.path.join(TMPL_DIR, file + ":See_Also")]
+    if not see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
+        see_also = ''
+    else:
+        see_also = ConvertMarkDown("%s:See_Also" % title, see_also)
+        logging.info("Found see_also: %s", see_also)
+
+    if see_also:
+        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
+
+    stability = SymbolDocs[os.path.join(TMPL_DIR, file + ":Stability_Level")]
+    if not stability or re.search(r'^\s*$', stability):
+        stability = ''
+    else:
+        stability = ParseStabilityLevel(stability, file, line_number, "Section stability level")
+        logging.info("Found stability: %s", stability)
+
+    if stability:
+        AnnotationsUsed[stability] = 1
+        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (section_id, stability)
+    elif DEFAULT_STABILITY:
+        AnnotationsUsed[DEFAULT_STABILITY] = 1
+        stability = "<refsect1 id=\"%s.stability-level\">\n<title>Stability 
Level</title>\n<acronym>%s</acronym>, unless otherwise indicated\n</refsect1>\n" % (section_id, 
DEFAULT_STABILITY)
+
+
+    image = SymbolDocs[os.path.join(TMPL_DIR, file + ":Image")]
+    if image or re.search(r'^\s*$', image):
+        image = ''
+    else:
+        image = image.strip()
+
+        format = None
+
+        il = image.lower()
+        if re.search(r'jpe?g$', il):
+            format = "format='JPEG'"
+        elif il.endswith('png'):
+            format = "format='PNG'"
+        elif il.endswith('svg'):
+            format = "format='SVG'"
+        else:
+            format = ''
+
+
+        image = "  <inlinegraphic fileref='%s' %s/>\n" % (image, format)
+
+    include_output = ''
+    if includes:
+        include_output += "<refsect1 id=\"%s.includes\"><title>Includes</title><synopsis>" % section_id
+        for include in includes.split(','):
+            if re.search(r'^\".+\"$', include):
+                include_output += "#include %s\n" % include
+            else:
+                include = re.sub(r'^\s+|\s+$', '', include, flags=re.S)
+                include_output += "#include &lt;%s&gt;\n" % include
+
+
+        include_output += "</synopsis></refsect1>\n"
+
+
+    extralinks = OutputSectionExtraLinks(title, "Section:%s" % file)
+
+    old_db_file = os.path.join(DB_OUTPUT_DIR, file + '.xml')
+    new_db_file = os.path.join(DB_OUTPUT_DIR, file, '.xml.new')
+
+    OUTPUT = open(new_db_file, 'w')
+
+    object_anchors = ''
+    for fobject in file_objects:
+        if fobject == section_id:
+            continue
+        sid = common.CreateValidSGMLID(fobject)
+        logging.info("Adding anchor for %s\n", fobject)
+        object_anchors += "<anchor id=\"%s\"/>" % sid
 
     # Make sure we produce valid docbook
-    $$functions_details ||= "<para />";
+    if not functions_details:
+        functions_details = "<para />"
 
-    # We used to output this, but is messes up our UpdateFileIfChanged code
+    # We used to output this, but is messes up our common.UpdateFileIfChanged code
     # since it changes every day (and it is only used in the man pages):
     # "<refentry id="$section_id" revision="$mday $month $year">"
 
-    print OUTPUT <<EOF;
-${\( MakeDocHeader ("refentry") )}
+    OUTPUT.write(r'''${\( MakeDocHeader ("refentry") )
 <refentry id="$section_id">
 <refmeta>
-<refentrytitle role="top_of_page" id="$section_id.top_of_page">$title</refentrytitle>
+<refentrytitle role="top_of_page" id="%s.top_of_page">%s</refentrytitle>
 <manvolnum>3</manvolnum>
-<refmiscinfo>\U$MODULE\E Library$image</refmiscinfo>
+<refmiscinfo>\U%s\E Library%s</refmiscinfo>
 </refmeta>
 <refnamediv>
-<refname>$title</refname>
-<refpurpose>$short_desc</refpurpose>
+<refname>%s</refname>
+<refpurpose>%s</refpurpose>
 </refnamediv>
-$stability
-$$functions_synop$$args_synop$$signals_synop$object_anchors$$other_synop$$hierarchy$$prerequisites$$derived$$interfaces$$implementations
+%s
+%s%s%s%s%s%s%s%s%s%s
 $include_output
-<refsect1 id="$section_id.description" role="desc">
+<refsect1 id="%s.description" role="desc">
 <title role="desc.title">Description</title>
-$extralinks$long_desc
+%s%s
 </refsect1>
-<refsect1 id="$section_id.functions_details" role="details">
+<refsect1 id="%s.functions_details" role="details">
 <title role="details.title">Functions</title>
-$$functions_details
+%s
 </refsect1>
-<refsect1 id="$section_id.other_details" role="details">
+<refsect1 id="%s.other_details" role="details">
 <title role="details.title">Types and Values</title>
-$$other_details
+%s
 </refsect1>
-$$args_desc$$signals_desc$see_also
+%s%s%s
 </refentry>
-EOF
-    close (OUTPUT);
+''' % (section_id, title, MODULE, image, title, short_desc, stability, functions_synop, args_synop, 
signals_synop, object_anchors, other_synop, hierarchy, prerequisites, derived, interfaces, implementations, 
section_id, extralinks, long_desc, section_id, functions_details, section_id, other_details, args_desc, 
signals_desc, see_also))
+    OUTPUT.close()
+
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
 
-    return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
-}
 
 #############################################################################
 # Function    : OutputProgramDBFile
@@ -2593,152 +2407,141 @@ EOF
 #               $section_id - the id to use for the toplevel tag.
 #############################################################################
 
-sub OutputProgramDBFile {
-    my ($program, $section_id) = @_;
-
-    @TRACE@("Output program docbook for $program\n");
+def OutputProgramDBFile(program, section_id):
+    logging.info("Output program docbook for %s", program)
 
-    my $short_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Short_Description"};
-    if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
-        $short_desc = "";
-    } else {
+    short_desc = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Short_Description")]
+    if not short_desc or re.search(r'^\s*$', short_desc):
+        short_desc = ''
+    else:
         # Don't use ConvertMarkDown here for now since we don't want blocks
-        $short_desc = &ExpandAbbreviations("$program", $short_desc);
-        @TRACE@("Found short_desc: $short_desc");
-    }
-
-    my $synopsis = $SourceSymbolDocs{"$TMPL_DIR/$program:Synopsis"};
-    if (defined ($synopsis) && $synopsis !~ m/^\s*$/) {
-        my $i;
-        my @items = split(' ', $synopsis);
-        for ($i = 0; $i <= $#items; $i++) {
-            my $parameter = $items[$i];
-            my $choice = "plain";
-            my $rep = "";
+        short_desc = ExpandAbbreviations(program, short_desc)
+        logging.info("Found short_desc: %s", short_desc)
+
+    synopsis = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Synopsis")]
+    if synopsis and re.search(r'~\s*$', synopsis):
+        items = synopsis.split(' ')
+        for i in range(0, len(items)):
+            parameter = items[i]
+            choice = "plain"
+            rep = ''
 
             # first parameter is the command name
-            if ($i == 0) {
-                $synopsis = "<command>$parameter</command>\n";
-                next;
-            }
+            if i == 0:
+                synopsis = "<command>%s</command>\n" % parameter
+                continue
 
             # square brackets indicate optional parameters, curly brackets
             # indicate required parameters ("plain" parameters are also
             # mandatory, but do not get extra decoration)
-            if ($parameter =~ s/^\[(.+?)\]$/$1/) {
-                $choice = "opt";
-            } elsif ($parameter =~ s/^\{(.+?)\}$/$1/) {
-                $choice = "req";
-            }
+            m1 = re.search(r'^\[(.+?)\]$', parameter)
+            m2 = re.search(r'^\{(.+?)\}$', parameter)
+            m3 = re.search(r'\.\.\.$', parameter)
+            m4 = re.search(r'\*(.+?)\*', parameter)
+            if m1:
+                choice = "opt"
+            elif m2:
+                choice = "req"
 
             # parameters ending in "..." are repeatable
-            if ($parameter =~ s/\.\.\.$//) {
-                $rep = " rep=\"repeat\"";
-            }
+            if m3:
+                rep = ' rep=\"repeat\"'
 
             # italic parameters are replaceable parameters
-            if ($parameter =~ s/\*(.+?)\*/$1/) {
-                $parameter = "<replaceable>$parameter</replaceable>";
-            }
-
-            $synopsis .= "<arg choice=\"$choice\"$rep>";
-            $synopsis .= $parameter;
-            $synopsis .= "</arg>\n";
-        }
-
-        @TRACE@("Found synopsis: $synopsis");
-    } else {
-        $synopsis = "<command>$program</command>";
-    }
-
-    my $long_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Long_Description"};
-    if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
-        $long_desc = "";
-    } else {
-        $long_desc = &ConvertMarkDown("$program:Long_description", $long_desc);
-        @TRACE@("Found long_desc: $long_desc");
-    }
-
-    my $options = "";
-    if (defined ($SourceSymbolDocs{"$TMPL_DIR/$program:Options"})) {
-        my @opts = @{ $SourceSymbolDocs{"$TMPL_DIR/$program:Options"} };
-        my $k;
-
-        $options = "<refsect1>\n<title>Options</title>\n<variablelist>\n";
-        for ($k = 0; $k <= $#opts; $k += 2) {
-            my $opt_desc = $opts[$k+1];
-            my @opt_names;
-            my $i;
-
-            $opt_desc =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
-
-            $options .= "<varlistentry>\n<term>";
-            @opt_names = split (',', $opts[$k]);
-            for ($i = 0; $i <= $#opt_names; $i++) {
-                my $prefix = ($i > 0) ? ", " : "";
-                $opt_names[$i] =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
-
-                $options .= "$prefix<option>$opt_names[$i]</option>\n";
-            }
-            $options .= "</term>\n";
-            $options .= "<listitem><para>$opt_desc</para></listitem>\n";
-            $options .= "</varlistentry>\n";
-        }
-        $options .= "</variablelist></refsect1>\n";
-    }
-
-    my $exit_status = $SourceSymbolDocs{"$TMPL_DIR/$program:Returns"};
-    if (defined ($exit_status) && $exit_status ne "") {
-        $exit_status = &ConvertMarkDown("$program:Returns", $exit_status);
-        $exit_status = "<refsect1 id=\"$section_id.exit-status\">\n<title>Exit 
Status</title>\n$exit_status\n</refsect1>\n";
-    } else {
-        $exit_status = "";
-    }
-
-    my $see_also = $SourceSymbolDocs{"$TMPL_DIR/$program:See_Also"};
-    if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
-        $see_also = "";
-    } else {
-        $see_also = &ConvertMarkDown("$program:See_Also", $see_also);
-        @TRACE@("Found see_also: $see_also");
-    }
-    if ($see_also) {
-        $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See 
Also</title>\n$see_also\n</refsect1>\n";
-    }
-
-    my $old_db_file = "$DB_OUTPUT_DIR/$program.xml";
-    my $new_db_file = "$DB_OUTPUT_DIR/$program.xml.new";
-
-    open (OUTPUT, ">$new_db_file")
-        || die "Can't create $new_db_file: $!";
-
-    print OUTPUT <<EOF;
-${\( MakeDocHeader ("refentry") )}
-<refentry id="$section_id">
+            if m4:
+                parameter = "<replaceable>%s</replaceable>" % parameter
+
+
+            synopsis += "<arg choice=\"%s\"%s>" % (choice, rep)
+            synopsis += parameter
+            synopsis += "</arg>\n"
+
+        logging.info("Found synopsis: %s", synopsis)
+    else:
+        synopsis = "<command>%s</command>" % program
+
+
+    long_desc = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Long_Description")]
+    if not long_desc or re.search(r'^\s*$', long_desc):
+        long_desc = ''
+    else:
+        long_desc = ConvertMarkDown("%s:Long_description" % program, long_desc)
+        logging.info("Found long_desc: %s", long_desc)
+
+    options = ''
+    o = os.path.join(TMPL_DIR, program + ":Options")
+    if o in SourceSymbolDocs:
+        opts = SourceSymbolDocs[o]
+
+        options = "<refsect1>\n<title>Options</title>\n<variablelist>\n"
+        for k in range(0, len(opts), 2):
+            opt_desc = opts[k+1]
+
+            opt_desc = re.sub(r'\*(.+?)\*', r'<replaceable>\1<\/replaceable>', opt_desc)
+
+            options += "<varlistentry>\n<term>"
+            opt_names = opts[k].split(' ')
+            for i in range(len(opt_names)):
+                prefix = ', ' if i > 0 else ''
+                opt_names[i] = re.sub(r'\*(.+?)\*', r'<replaceable>\1<\/replaceable>', opt_names[i])
+
+                options += "%s<option>%s</option>\n" % (prefix, opt_names[i])
+
+            options += "</term>\n"
+            options += "<listitem><para>%s</para></listitem>\n" % opt_desc
+            options += "</varlistentry>\n"
+
+        options += "</variablelist></refsect1>\n"
+
+
+    exit_status = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":Returns")]
+    if exit_status and exit_status != '':
+        exit_status = ConvertMarkDown("%s:Returns" % program, exit_status)
+        exit_status = "<refsect1 id=\"%s.exit-status\">\n<title>Exit Status</title>\n%s\n</refsect1>\n" % 
(section_id, exit_status)
+    else:
+        exit_status = ''
+
+
+    see_also = SourceSymbolDocs[os.path.join(TMPL_DIR, program + ":See_Also")]
+    if see_also or re.search(r'^\s*(<para>)?\s*(</para>)?\s*$', see_also):
+        see_also = ''
+    else:
+        see_also = ConvertMarkDown("%s:See_Also" % program, see_also)
+        logging.info("Found see_also: %s", see_also)
+
+    if see_also:
+        see_also = "<refsect1 id=\"%s.see-also\">\n<title>See Also</title>\n%s\n</refsect1>\n" % 
(section_id, see_also)
+
+
+    old_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml")
+    new_db_file = os.path.join(DB_OUTPUT_DIR, program + ".xml.new")
+
+    OUTPUT = open(new_db_file, 'w')
+
+    OUTPUT.write('''%s
+<refentry id="%s">
 <refmeta>
-<refentrytitle role="top_of_page" id="$section_id.top_of_page">$program</refentrytitle>
+<refentrytitle role="top_of_page" id="%s.top_of_page">$program</refentrytitle>
 <manvolnum>1</manvolnum>
 <refmiscinfo>User Commands</refmiscinfo>
 </refmeta>
 <refnamediv>
-<refname>$program</refname>
-<refpurpose>$short_desc</refpurpose>
+<refname>%s</refname>
+<refpurpose>%s</refpurpose>
 </refnamediv>
 <refsynopsisdiv>
-<cmdsynopsis>$synopsis</cmdsynopsis>
+<cmdsynopsis>%s</cmdsynopsis>
 </refsynopsisdiv>
-<refsect1 id="$section_id.description" role="desc">
+<refsect1 id="%s.description" role="desc">
 <title role="desc.title">Description</title>
-$long_desc
+%s
 </refsect1>
-$options$exit_status$see_also
+%s%s%s
 </refentry>
-EOF
-    close (OUTPUT);
-
-    return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
-}
+''' % (MakeDocHeader("refentry"), section_id, section_id, program, short_desc, synopsis, section_id, 
long_desc, options, exit_status, see_also))
+    OUTPUT.close()
 
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
 
 
 #############################################################################
@@ -2747,33 +2550,22 @@ EOF
 #               expanding abbreviations
 # Arguments   : $file - the source file.
 #############################################################################
-sub OutputExtraFile {
-    my ($file) = @_;
-
-    my $basename;
+def OutputExtraFile(file):
 
-    ($basename = $file) =~ s!^.*/!!;
+    basename = re.sub(r'^.*/', '', file)
 
-    my $old_db_file = "$DB_OUTPUT_DIR/$basename";
-    my $new_db_file = "$DB_OUTPUT_DIR/$basename.new";
+    old_db_file = os.path.join(DB_OUTPUT_DIR, basename)
+    new_db_file = os.path.join(DB_OUTPUT_DIR, basename + ".new")
 
-    my $contents;
+    contents = open(file).read()
 
-    open(EXTRA_FILE, "<$file") || die "Can't open $file";
+    OUTPUT = open(new_db_file, 'w')
 
-    {
-        local $/;
-        $contents = <EXTRA_FILE>;
-    }
+    OUTPUT.write(ExpandAbbreviations(basename + " file", contents))
+    OUTPUT.close()
 
-    open (OUTPUT, ">$new_db_file")
-        || die "Can't create $new_db_file: $!";
+    return common.UpdateFileIfChanged(old_db_file, new_db_file, 0)
 
-    print OUTPUT &ExpandAbbreviations ("$basename file", $contents);
-    close (OUTPUT);
-
-    return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
-}
 #############################################################################
 # Function    : OutputBook
 # Description : Outputs the entities that need to be included into the
@@ -2784,39 +2576,34 @@ sub OutputExtraFile {
 #                  added in the main docbook file at the desired position.
 #############################################################################
 
-sub OutputBook {
-    my ($book_top, $book_bottom) = @_;
+def OutputBook(book_top, book_bottom):
 
-    my $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.top";
-    my $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.top.new";
+    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top")
+    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.top.new")
 
-    open (OUTPUT, ">$new_file")
-        || die "Can't create $new_file: $!";
-    print OUTPUT $book_top;
-    close (OUTPUT);
+    OUTPUT = open(new_file, 'w')
+    OUTPUT.write(book_top)
+    OUTPUT.close()
 
-    &UpdateFileIfChanged ($old_file, $new_file, 0);
+    common.UpdateFileIfChanged(old_file, new_file, 0)
 
 
-    $old_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom";
-    $new_file = "$DB_OUTPUT_DIR/$MODULE-doc.bottom.new";
+    old_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom")
+    new_file = os.path.join(DB_OUTPUT_DIR, MODULE + "-doc.bottom.new")
 
-    open (OUTPUT, ">$new_file")
-        || die "Can't create $new_file: $!";
-    print OUTPUT $book_bottom;
-    close (OUTPUT);
+    OUTPUT = open(new_file, 'w')
+    OUTPUT.write(book_bottom)
+    OUTPUT.close()
 
-    &UpdateFileIfChanged ($old_file, $new_file, 0);
+    common.UpdateFileIfChanged(old_file, new_file, 0)
 
 
     # If the main docbook file hasn't been created yet, we create it here.
     # The user can tweak it later.
-    if ($MAIN_SGML_FILE && ! -e $MAIN_SGML_FILE) {
-        open (OUTPUT, ">$MAIN_SGML_FILE")
-          || die "Can't create $MAIN_SGML_FILE: $!";
+    if MAIN_SGML_FILE and not os.path.exists(MAIN_SGML_FILE):
+        OUTPUT = open(MAIN_SGML_FILE, 'w')
 
-        print OUTPUT <<EOF;
-${\( MakeDocHeader ("book") )}
+        OUTPUT.write('''
 <book id="index">
   <bookinfo>
     <title>&package_name; Reference Manual</title>
@@ -2831,26 +2618,23 @@ ${\( MakeDocHeader ("book") )}
     <title>[Insert title here]</title>
     $book_bottom
   </chapter>
-EOF
-        if (-e $OBJECT_TREE_FILE) {
-            print OUTPUT <<EOF;
-  <chapter id="object-tree">
+''' % MakeDocHeader("book"))
+        if os.path.exists(OBJECT_TREE_FILE):
+            OUTPUT.write('''  <chapter id="object-tree">
     <title>Object Hierarchy</title>
     <xi:include href="xml/tree_index.sgml"/>
   </chapter>
-EOF
-        } else {
-            print OUTPUT <<EOF;
-  <!-- enable this when you use gobject types
+''')
+        else:
+            OUTPUT.write('''  <!-- enable this when you use gobject types
   <chapter id="object-tree">
     <title>Object Hierarchy</title>
     <xi:include href="xml/tree_index.sgml"/>
   </chapter>
   -->
-EOF
-        }
-        print OUTPUT <<EOF;
-  <index id="api-index-full">
+''')
+
+        OUTPUT.write('''  <index id="api-index-full">
     <title>API Index</title>
     <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
   </index>
@@ -2858,26 +2642,21 @@ EOF
     <title>Index of deprecated API</title>
     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
   </index>
-EOF
-        if (keys(%AnnotationsUsed)) {
-            print OUTPUT <<EOF;
-  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
-EOF
-        } else {
-            print OUTPUT <<EOF;
-  <!-- enable this when you use gobject introspection annotations
+''')
+        if len(AnnotationsUsed) > 0:
+            OUTPUT.write('''  <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+''')
+        else:
+            OUTPUT.write('''  <!-- enable this when you use gobject introspection annotations
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
   -->
-EOF
-        }
-        print OUTPUT <<EOF;
-</book>
-EOF
+''')
 
-        close (OUTPUT);
-    }
-}
+        OUTPUT.write('''
+</book>
+''')
 
+        OUTPUT.close()
 
 #############################################################################
 # Function    : CreateValidSGML
@@ -2886,15 +2665,14 @@ EOF
 # Arguments   : $text - the text to turn into proper SGML.
 #############################################################################
 
-sub CreateValidSGML {
-    my ($text) = @_;
-    $text =~ s/&/&amp;/g;        # Do this first, or the others get messed up.
-    $text =~ s/</&lt;/g;
-    $text =~ s/>/&gt;/g;
-    # browers render single tabs inconsistently
-    $text =~ s/([^\s])\t([^\s])/$1&#160;$2/g;
-    return $text;
-}
+def CreateValidSGML(text):
+    text = re.sub(r'&', r'&amp;', text)        # Do this first, or the others get messed up.
+    text = re.sub(r'<', r'&lt;', text)
+    text = re.sub(r'>', r'&gt;', text)
+    # browsers render single tabs inconsistently
+    text = re.sub(r'([^\s])\t([^\s])', r'\1&#160;\2', text)
+    return text
+
 
 #############################################################################
 # Function    : ConvertSGMLChars
@@ -2906,77 +2684,63 @@ sub CreateValidSGML {
 # Arguments   : $text - the text to turn into proper SGML.
 #############################################################################
 
-sub ConvertSGMLChars {
-    my ($symbol, $text) = @_;
+def ConvertSGMLChars(symbol, text):
 
-    if ($INLINE_MARKUP_MODE) {
+    if INLINE_MARKUP_MODE:
         # For the XML/SGML mode only convert to entities outside CDATA sections.
-        return &ModifyXMLElements ($text, $symbol,
-                                   "<!\\[CDATA\\[|<programlisting[^>]*>",
-                                   \&ConvertSGMLCharsEndTag,
-                                   \&ConvertSGMLCharsCallback);
-    } else {
-        # For the simple non-sgml mode, convert to entities everywhere.
-
-       # First, convert freestanding & to &amp;
-        $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;
-        $text =~ s/</&lt;/g;
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[|<programlisting[^>]*>",
+                                 ConvertSGMLCharsEndTag,
+                                 ConvertSGMLCharsCallback)
+    # For the simple non-sgml mode, convert to entities everywhere.
+
+    # First, convert freestanding & to &amp
+    text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)
+    text = re.sub(r'<', r'/&lt', text)
+    # Allow ">" at beginning of string for blockquote markdown
+    text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
+
+    return text
+
+def ConvertSGMLCharsEndTag(t):
+    if t == r'<!\[CDATA\[':
+        return "]]>"
+    return "</programlisting>"
+
+def ConvertSGMLCharsCallback(text, symbol, tag):
+    if re.search(r'^<programlisting', tag):
+        # We can handle <programlisting> specially here.
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[",
+                                 ConvertSGMLCharsEndTag,
+                                 ConvertSGMLCharsCallback2)
+    elif tag == '':
+        # If we're not in CDATA convert to entities.
+        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
+        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
         # Allow ">" at beginning of string for blockquote markdown
-        $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
+        text = re.sub(r'''(?<=[^\w\n"'\/-])>''', r'&gt;', text)
 
-        return $text;
-    }
-}
+        # Handle "#include <xxxxx>"
+        text = re.sub(r'#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
 
+    return text
 
-sub ConvertSGMLCharsEndTag {
-  if ($_[0] eq "<!\[CDATA\[") {
-    return "]]>";
-  } else {
-    return "</programlisting>";
-  }
-}
 
-sub ConvertSGMLCharsCallback {
-  my ($text, $symbol, $tag) = @_;
+def ConvertSGMLCharsCallback2(text, symbol, tag):
 
-  if ($tag =~ m/^<programlisting/) {
-    # We can handle <programlisting> specially here.
-    return &ModifyXMLElements ($text, $symbol,
-                               "<!\\[CDATA\\[",
-                               \&ConvertSGMLCharsEndTag,
-                               \&ConvertSGMLCharsCallback2);
-  } elsif ($tag eq "") {
     # If we're not in CDATA convert to entities.
-    $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
-    $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
-    # Allow ">" at beginning of string for blockquote markdown
-    $text =~ s/(?<=[^\w\n"'\/-])>/&gt;/g;
-
-    # Handle "#include <xxxxx>"
-    $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
-  }
+    # We could handle <programlisting> differently, though I'm not sure it helps.
+    if tag == '':
+        # replace only if its not a tag
+        text = re.sub(r'&(?![a-zA-Z#]+;)', r'&amp;', text)        # Do this first, or the others get messed 
up.
+        text = re.sub(r'<(?![a-zA-Z\/!])', r'&lt;', text)
+        text = re.sub(r'''(?<![a-zA-Z0-9"'\/-])>''', r'&gt;', text)
+        # Handle "#include <xxxxx>"
+        text = re.sub(r'/#include(\s+)<([^>]+)>', r'#include\1&lt;\2&gt;', text)
 
-  return $text;
-}
-
-sub ConvertSGMLCharsCallback2 {
-  my ($text, $symbol, $tag) = @_;
-
-  # If we're not in CDATA convert to entities.
-  # We could handle <programlisting> differently, though I'm not sure it helps.
-  if ($tag eq "") {
-    # replace only if its not a tag
-    $text =~ s/&(?![a-zA-Z#]+;)/&amp;/g;        # Do this first, or the others get messed up.
-    $text =~ s/<(?![a-zA-Z\/!])/&lt;/g;
-    $text =~ s/(?<![a-zA-Z0-9"'\/-])>/&gt;/g;
+    return text
 
-    # Handle "#include <xxxxx>"
-    $text =~ s/#include(\s+)<([^>]+)>/#include$1&lt;$2&gt;/g;
-  }
-
-  return $text;
-}
 
 #############################################################################
 # Function    : ExpandAnnotation
@@ -2984,55 +2748,51 @@ sub ConvertSGMLCharsCallback2 {
 # Arguments   : $symbol - the symbol being documented, for error messages.
 #                $text - the text to expand.
 #############################################################################
-sub ExpandAnnotation {
-    my ($symbol, $param_desc) = @_;
-    my $param_annotations = "";
+def ExpandAnnotation(symbol, param_desc):
+    param_annotations = ''
 
     # look for annotations at the start of the comment part
     # function level annotations don't end with a colon ':'
-    if ($param_desc =~ m%^\s*\((.*?)\)(:|$)%) {
-        my @annotations;
-        my $annotation;
-        $param_desc = $';
-
-        @annotations = split(/\)\s*\(/,$1);
-        @TRACE@("annotations for $symbol: '$1'\n");
-        foreach $annotation (@annotations) {
+    m = re.search(r'^\s*\((.*?)\)(:|$)', param_desc)
+    if m:
+        param_desc = param_desc[m.end():]
+
+        annotations = re.split(r'\)\s*\(', m.group(1))
+        logging.info("annotations for %s: '%s'\n", symbol, m.group(1))
+        for annotation in annotations:
             # need to search for the longest key-match in %AnnotationDefinition
-            my $match_length=0;
-            my $match_annotation="";
-            my $annotationdef;
-            foreach $annotationdef (keys %AnnotationDefinition) {
-                if ($annotation =~ m/^$annotationdef/) {
-                    if (length($annotationdef)>$match_length) {
-                        $match_length=length($annotationdef);
-                        $match_annotation=$annotationdef;
-                    }
-                }
-            }
-            my $annotation_extra = "";
-            if ($match_annotation ne "") {
-                if ($annotation =~ m%$match_annotation\s+(.*)%) {
-                    $annotation_extra = " $1";
-                }
-                $AnnotationsUsed{$match_annotation} = 1;
-                $param_annotations .= "[<acronym>$match_annotation</acronym>$annotation_extra]";
-            }
-            else {
-                &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                    "unknown annotation \"$annotation\" in documentation for $symbol.");
-                $param_annotations .= "[$annotation]";
-            }
-        }
-        chomp($param_desc);
-        $param_desc =~ m/^(.*?)\.*\s*$/s;
-        $param_desc = "$1. ";
-    }
-    if ($param_annotations ne "") {
-        $param_annotations = "<emphasis role=\"annotation\">$param_annotations</emphasis>";
-    }
-    return ($param_desc, $param_annotations);
-}
+            match_length = 0
+            match_annotation = ''
+
+            for annotationdef in AnnotationDefinition:
+                if annotation.startswith(annotationdef):
+                    if len(annotationdef) > match_length:
+                        match_length = len(annotationdef)
+                        match_annotation = annotationdef
+
+            annotation_extra = ''
+            if match_annotation != '':
+                m = re.search(match_annotation + r'\s+(.*)', annotation)
+                if m:
+                    annotation_extra = " " + m.group(1)
+
+                AnnotationsUsed[match_annotation] = 1
+                param_annotations += "[<acronym>%s</acronym>%s]" % (match_annotation, annotation_extra)
+            else:
+                common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                  "unknown annotation \"%s\" in documentation for %s." % (annotation, 
symbol))
+                param_annotations += "[%s]" % annotation
+
+
+        param_desc = param_desc.strip()
+        m = re.search(r'^(.*?)\.*\s*$', param_desc)
+        param_desc = "%s. " % m.group(1)
+
+    if param_annotations != '':
+        param_annotations = "<emphasis role=\"annotation\">%s</emphasis>" % param_annotations
+
+    return (param_desc, param_annotations)
+
 
 #############################################################################
 # Function    : ExpandAbbreviations
@@ -3043,133 +2803,130 @@ sub ExpandAnnotation {
 #                $text - the text to expand.
 #############################################################################
 
-sub ExpandAbbreviations {
-  my ($symbol, $text) = @_;
-
-  # Note: This is a fallback and normally done in the markdown parser
+def ExpandAbbreviations(symbol, text):
+    # Note: This is a fallback and normally done in the markdown parser
 
-  # Convert "|[" and "]|" into the start and end of program listing examples.
-  # Support \[<!-- language="C" --> modifiers
-  $text =~ s%\|\[<!-- language="([^"]+)" -->%<informalexample><programlisting language="$1"><![CDATA[%g;
-  $text =~ s%\|\[%<informalexample><programlisting><![CDATA[%g;
-  $text =~ s%\]\|%]]></programlisting></informalexample>%g;
+    # Convert "|[" and "]|" into the start and end of program listing examples.
+    # Support \[<!-- language="C" --> modifiers
+    text = re.sub(r'\|\[<!-- language="([^"]+)" -->', r'<informalexample><programlisting 
language="\1"><![CDATA[', text)
+    text = re.sub(r'\|\[', r'<informalexample><programlisting><![CDATA[', text)
+    text = re.sub(r'\]\|', r']]></programlisting></informalexample>', text)
 
-  # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
-  # as such)
-  return &ModifyXMLElements ($text, $symbol,
+    # keep CDATA unmodified, preserve ulink tags (ideally we preseve all tags
+    # as such)
+    return ModifyXMLElements(text, symbol,
                              "<!\\[CDATA\\[|<ulink[^>]*>|<programlisting[^>]*>|<!DOCTYPE",
-                             \&ExpandAbbreviationsEndTag,
-                             \&ExpandAbbreviationsCallback);
-}
+                             ExpandAbbreviationsEndTag,
+                             ExpandAbbreviationsCallback)
 
 
 # Returns the end tag (as a regexp) corresponding to the given start tag.
-sub ExpandAbbreviationsEndTag {
-  my ($start_tag) = @_;
-
-  if ($start_tag eq "<!\[CDATA\[") {
-    return "]]>";
-  } elsif ($start_tag eq "<!DOCTYPE") {
-    return ">";
-  } elsif ($start_tag =~ m/<(\w+)/) {
-    return "</$1>";
-  }
-}
+def ExpandAbbreviationsEndTag(start_tag):
+    if start_tag == r'<!\[CDATA\[':
+        return "]]>"
+    if start_tag == "<!DOCTYPE":
+        return ">"
+    m = re.search(r'<(\w+)', start_tag)
+    return "</%s>" % m.group(1)
 
 # Called inside or outside each CDATA or <programlisting> section.
-sub ExpandAbbreviationsCallback {
-  my ($text, $symbol, $tag) = @_;
-
-  if ($tag =~ m/^<programlisting/) {
-    # Handle any embedded CDATA sections.
-    return &ModifyXMLElements ($text, $symbol,
-                               "<!\\[CDATA\\[",
-                               \&ExpandAbbreviationsEndTag,
-                               \&ExpandAbbreviationsCallback2);
-  } elsif ($tag eq "") {
-    # NOTE: this is a fallback. It is normally done by the Markdown parser.
-
-    # We are outside any CDATA or <programlisting> sections, so we expand
-    # any gtk-doc abbreviations.
-
-    # Convert '@param()'
-    # FIXME: we could make those also links ($symbol.$2), but that would be less
-    # useful as the link target is a few lines up or down
-    $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/$1<parameter>$2()<\/parameter>/g;
+def ExpandAbbreviationsCallback(text, symbol, tag):
+
+    if tag.startswith(r'^<programlisting'):
+        # Handle any embedded CDATA sections.
+        return ModifyXMLElements(text, symbol,
+                                 "<!\\[CDATA\\[",
+                                 ExpandAbbreviationsEndTag,
+                                 ExpandAbbreviationsCallback2)
+    elif tag == '':
+        # NOTE: this is a fallback. It is normally done by the Markdown parser.
+
+        # We are outside any CDATA or <programlisting> sections, so we expand
+        # any gtk-doc abbreviations.
+
+        # Convert '@param()'
+        # FIXME: we could make those also links ($symbol.$2), but that would be less
+        # useful as the link target is a few lines up or down
+        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', r'\1<parameter>\2()', text)
+
+        # Convert 'function()' or 'macro()'.
+        # if there is abc_*_def() we don't want to make a link to _def()
+        # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
+        def f1(m):
+            return '%s.%s;' % (m.group(1), MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")))
+        text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f1, text)
+        # handle #Object.func()
+        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', f1, text)
 
-    # Convert 'function()' or 'macro()'.
-    # if there is abc_*_def() we don't want to make a link to _def()
-    # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
-    $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
-    # handle #Object.func()
-    $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
+        # Convert '@param', but not '\@param'.
+        text = re.sub(r'(\A|[^\\])\@(\w+((\.|->)\w+)*)', r'\1<parameter>\2<\/parameter>', text)
+        text = re.sub(r'/\\\@', r'\@', text)
 
-    # Convert '@param', but not '\@param'.
-    $text =~ s/(\A|[^\\])\@(\w+((\.|->)\w+)*)/$1<parameter>$2<\/parameter>/g;
-    $text =~ s/\\\@/\@/g;
+        # Convert '%constant', but not '\%constant'.
+        # Also allow negative numbers, e.g. %-1.
+        def f2(m):
+            return '%s.%s;' % (m.group(1), MakeXRef(m.group(2), tagify(m.group(2), "literal")))
+        text = re.sub(r'(\A|[^\\])\%(-?\w+)', f2, text)
+        text = re.sub(r'\\\%', r'\%', text)
 
-    # Convert '%constant', but not '\%constant'.
-    # Also allow negative numbers, e.g. %-1.
-    $text =~ s/(\A|[^\\])\%(-?\w+)/$1.&MakeXRef($2, &tagify($2, "literal"));/eg;
-    $text =~ s/\\\%/\%/g;
+        # Convert '#symbol', but not '\#symbol'.
+        def f3(m):
+            return '%s.%s;' % (m.group(1), MakeHashXRef(m.group(2), "type"))
+        text = re.sub(r'(\A|[^\\])#([\w\-:\.]+[\w]+)', f3, text)
+        text = re.sub(r'\\#', '#', text)
 
-    # Convert '#symbol', but not '\#symbol'.
-    $text =~ s/(\A|[^\\])#([\w\-:\.]+[\w]+)/$1.&MakeHashXRef($2, "type");/eg;
-    $text =~ s/\\#/#/g;
-  }
 
-  return $text;
-}
+    return text
+
 
 # This is called inside a <programlisting>
-sub ExpandAbbreviationsCallback2 {
-  my ($text, $symbol, $tag) = @_;
-
-  if ($tag eq "") {
-    # We are inside a <programlisting> but outside any CDATA sections,
-    # so we expand any gtk-doc abbreviations.
-    # FIXME: why is this different from &ExpandAbbreviationsCallback(),
-    #        why not just call it
-    $text =~ s/#(\w+)/&MakeHashXRef($1, "");/eg;
-  } elsif ($tag eq "<![CDATA[") {
-    # NOTE: this is a fallback. It is normally done by the Markdown parser.
-    $text = &ReplaceEntities ($text, $symbol);
-  }
-
-  return $text;
-}
+def ExpandAbbreviationsCallback2(text, symbol, tag):
+    if tag == '':
+        # We are inside a <programlisting> but outside any CDATA sections,
+        # so we expand any gtk-doc abbreviations.
+        # FIXME: why is this different from &ExpandAbbreviationsCallback(),
+        #        why not just call it
+        text = re.sub(r'#(\w+)', lambda m: '%s;' % MakeHashXRef(m.group(1), ''), text)
+    elif tag == "<![CDATA[":
+        # NOTE: this is a fallback. It is normally done by the Markdown parser.
+        text = ReplaceEntities(text, symbol)
+
 
-sub MakeHashXRef {
-    my ($symbol, $tag) = @_;;
-    my $text = $symbol;
+    return text
+
+
+def MakeHashXRef(symbol, tag):
+    text = symbol
 
     # Check for things like '#include', '#define', and skip them.
-    if ($PreProcessorDirectives{$symbol}) {
-      return "#$symbol";
-    }
+    if PreProcessorDirectives[symbol]:
+        return "#%s" % symbol
 
     # Get rid of special suffixes ('-struct','-enum').
-    $text =~ s/-struct$//;
-    $text =~ s/-enum$//;
+    text = re.sub(r'-struct$', '', text)
+    text = re.sub(r'-enum$', '', text)
 
     # If the symbol is in the form "Object::signal", then change the symbol to
     # "Object-signal" and use "signal" as the text.
-    if ($symbol =~ s/::/-/) {
-      $text = "“$'”";
-    }
+    if '::' in symbol:
+        o, s = symbol.split('::', 1)
+        symbol = '%s-%s' % (o, s)
+        text = s
 
     # If the symbol is in the form "Object:property", then change the symbol to
     # "Object--property" and use "property" as the text.
-    if ($symbol =~ s/:/--/) {
-      $text = "“$'”";
-    }
+    if '-' in symbol:
+        o, p = symbol.split(':', 1)
+        symbol = '%s--%s' % (o, p)
+        text = p
 
-    if ($tag ne "") {
-      $text = tagify ($text, $tag);
-    }
 
-    return &MakeXRef($symbol, $text);
-}
+    if tag != '':
+        text = tagify(text, tag)
+
+
+    return MakeXRef(symbol, text)
+
 
 
 #############################################################################
@@ -3188,57 +2945,55 @@ sub MakeHashXRef {
 #                      and should return the appropriate end tag string regexp.
 #               $callback - callback called with each part of the text. It is
 #                      called with a piece of text, the symbol being
-#                      documented, and the matched start tag or "" if the text
+#                      documented, and the matched start tag or '' if the text
 #                      is outside the XML elements being matched.
 #############################################################################
-sub ModifyXMLElements {
-    my ($text, $symbol, $start_tag_regexp, $end_tag_func, $callback) = @_;
-    my ($before_tag, $start_tag, $end_tag_regexp, $end_tag);
-    my $result = "";
-
-    while ($text =~ m/$start_tag_regexp/s) {
-      $before_tag = $`; # Prematch for last successful match string
-      $start_tag = $&;  # Last successful match
-      $text = $';       # Postmatch for last successful match string
-
-      $result .= &$callback ($before_tag, $symbol, "");
-      $result .= $start_tag;
-
-      # get the matching end-tag for current tag
-      $end_tag_regexp = &$end_tag_func ($start_tag);
-
-      if ($text =~ m/$end_tag_regexp/s) {
-        $before_tag = $`;
-        $end_tag = $&;
-        $text = $';
-
-        $result .= &$callback ($before_tag, $symbol, $start_tag);
-        $result .= $end_tag;
-      } else {
-        &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-            "Can't find tag end: $end_tag_regexp in docs for: $symbol.");
-        # Just assume it is all inside the tag.
-        $result .= &$callback ($text, $symbol, $start_tag);
-        $text = "";
-      }
-    }
+def ModifyXMLElements(text, symbol, start_tag_regexp, end_tag_func, callback):
+    before_tag = start_tag = end_tag_regexp = end_tag = None
+    result = ''
+
+    m = re.search(start_tag_regexp, text, flags=re.S)
+    while m:
+        before_tag = text[0:m.begin()] # Prematch for last successful match string
+        start_tag = m.group(0)      # Last successful match
+        text = text[m.end():]       # Postmatch for last successful match string
+
+        result += callback(before_tag, symbol, '')
+        result += start_tag
+
+        # get the matching end-tag for current tag
+        end_tag_regexp = end_tag_func(start_tag)
+
+        m2 = re.search(end_tag_regexp, text, flags=re.S)
+        if m2:
+            before_tag = text[0:m2.begin()]
+            end_tag = m2.group(0)
+            text = text[m2.end():]
+
+            result += callback(before_tag, symbol, start_tag)
+            result += end_tag
+        else:
+            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                              "Can't find tag end: %s in docs for: %s." % (end_tag_regexp, symbol))
+            # Just assume it is all inside the tag.
+            result += callback(text, symbol, start_tag)
+            text = ''
+        m = re.search(start_tag_regexp, text, flags=re.S)
 
     # Handle any remaining text outside the tags.
-    $result .= &$callback ($text, $symbol, "");
+    result += callback(text, symbol, '')
 
-    return $result;
-}
+    return result
+
+
+def noop(*args):
+    return args[0]
 
-sub noop {
-  return $_[0];
-}
 
 # Adds a tag around some text.
 # e.g tagify("Text", "literal") => "<literal>Text</literal>".
-sub tagify {
-   my ($text, $elem) = @_;
-   return "<" . $elem . ">" . $text . "</" . $elem . ">";
-}
+def tagify(text, elem):
+    return "<" + elem + ">" + +text + "</" + +elem + ">"
 
 #############################################################################
 # Function    : MakeDocHeader
@@ -3246,18 +3001,15 @@ sub tagify {
 # Arguments   : $tag - doctype tag
 #############################################################################
 
-sub MakeDocHeader {
-    my ($tag) = @_;
-    my $header = $doctype_header;
-    $header =~ s/<!DOCTYPE \w+/<!DOCTYPE $tag/;
+def MakeDocHeader(tag):
+    header = doctype_header
+    header = re.sub(r'<!DOCTYPE \w+', r'<!DOCTYPE ' + tag, header)
 
     # fix the path for book since this is one level up
-    if ($tag eq "book") {
-        $header =~ s#<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">#<!ENTITY % gtkdocentities SYSTEM 
\"$1\">#;
-    }
+    if tag == "book":
+        header = re.sub(r'<!ENTITY % gtkdocentities SYSTEM \"../([a-zA-Z./]+)\">', r'<!ENTITY % 
gtkdocentities SYSTEM \"\1\">', header)
+    return header
 
-    return $header;
-}
 
 
 #############################################################################
@@ -3269,30 +3021,23 @@ sub MakeDocHeader {
 #               $text - text text to put inside the XRef, defaults to $symbol
 #############################################################################
 
-sub MakeXRef {
-    my ($symbol, $text) = ($_[0], $_[1]);
-
-    $symbol =~ s/^\s+//;
-    $symbol =~ s/\s+$//;
+def MakeXRef(symbol, text=None):
+    symbol = symbol.strip()
 
-    if (!defined($text)) {
-        $text = $symbol;
+    if not text:
+        text = symbol
 
         # Get rid of special suffixes ('-struct','-enum').
-        $text =~ s/-struct$//;
-        $text =~ s/-enum$//;
-    }
+        text = re.sub(r'-struct$', '', text)
+        text = re.sub(r'-enum$', '', text)
 
-    if ($symbol =~ m/ /) {
-        return "$text";
-    }
+    if ' ' in symbol:
+        return text
 
-    @TRACE@("Getting type link for $symbol -> $text\n");
-
-    my $symbol_id = &CreateValidSGMLID ($symbol);
-    return "<link linkend=\"$symbol_id\">$text</link>";
-}
+    logging.info("Getting type link for %s -> %s\n", symbol, text)
 
+    symbol_id = common.CreateValidSGMLID(symbol)
+    return "<link linkend=\"%s\">%s</link>" % (symbol_id, text)
 
 #############################################################################
 # Function    : MakeIndexterms
@@ -3300,40 +3045,30 @@ sub MakeXRef {
 # Arguments   : $symbol - the symbol to create indexterms for
 #############################################################################
 
-sub MakeIndexterms {
-  my ($symbol, $id) = @_;
-  my $terms =  "";
-  my $sortas = "";
-
-  # make the index useful, by ommiting the namespace when sorting
-  if ($NAME_SPACE ne "") {
-    if ($symbol =~ m/^$NAME_SPACE\_?(.*)/i) {
-       $sortas=" sortas=\"$1\"";
-    }
-  }
-
-  if (exists $Deprecated{$symbol}) {
-      $terms .= "<indexterm zone=\"$id\" role=\"deprecated\"><primary$sortas>$symbol</primary></indexterm>";
-      $IndexEntriesDeprecated{$symbol}=$id;
-      $IndexEntriesFull{$symbol}=$id;
-  }
-  if (exists $Since{$symbol}) {
-     my $since = $Since{$symbol};
-     $since =~ s/^\s+//;
-     $since =~ s/\s+$//;
-     if ($since ne "") {
-         $terms .= "<indexterm zone=\"$id\" role=\"$since\"><primary$sortas>$symbol</primary></indexterm>";
-     }
-     $IndexEntriesSince{$symbol}=$id;
-     $IndexEntriesFull{$symbol}=$id;
-  }
-  if ($terms eq "") {
-     $terms .= "<indexterm zone=\"$id\"><primary$sortas>$symbol</primary></indexterm>";
-     $IndexEntriesFull{$symbol}=$id;
-  }
-
-  return $terms;
- }
+def MakeIndexterms(symbol, sid):
+    terms = ''
+    sortas = ''
+
+    # make the index useful, by ommiting the namespace when sorting
+    if NAME_SPACE != '':
+        m = re.search(r'^$NAME_SPACE\_?(.*)', symbol, flags=re.I)
+        if m:
+            sortas = ' sortas=\"%s\"' % m.group(1)
+    if symbol in Deprecated:
+        terms += "<indexterm zone=\"%s\" role=\"deprecated\"><primary%s>%s</primary></indexterm>" % (sid, 
sortas, symbol)
+        IndexEntriesDeprecated[symbol] = sid
+        IndexEntriesFull[symbol] = sid
+    if symbol in Since:
+        since = Since[symbol].strip()
+        if since != '':
+            terms += "<indexterm zone=\"%s\" role=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, since, 
sortas, symbol)
+        IndexEntriesSince[symbol] = sid
+        IndexEntriesFull[symbol] = sid
+    if terms == '':
+        terms += "<indexterm zone=\"%s\"><primary%s>%s</primary></indexterm>" % (sid, sortas, symbol)
+        IndexEntriesFull[symbol] = sid
+    return terms
+
 
 #############################################################################
 # Function    : MakeDeprecationNote
@@ -3341,32 +3076,30 @@ sub MakeIndexterms {
 # Arguments   : $symbol - the symbol to try to create a warning for.
 #############################################################################
 
-sub MakeDeprecationNote {
-    my ($symbol) = $_[0];
-    my $desc = "";
-    if (exists $Deprecated{$symbol}) {
-        my $note;
-
-        $desc .= "<warning><para><literal>$symbol</literal> ";
-
-        $note = $Deprecated{$symbol};
-
-        if ($note =~ /^\s*([0-9\.]+)\s*:?/) {
-                $desc .= "has been deprecated since version $1 and should not be used in newly-written 
code.</para>";
-        } else {
-                $desc .= "is deprecated and should not be used in newly-written code.</para>";
-        }
-        $note =~ s/^\s*([0-9\.]+)\s*:?\s*//;
-        $note =~ s/^\s+//;
-        $note =~ s/\s+$//;
-        if ($note ne "") {
-            $note = &ConvertMarkDown($symbol, $note);
-            $desc .= " " . $note;
-        }
-        $desc .= "</warning>\n";
-    }
-    return $desc;
-}
+def MakeDeprecationNote(symbol):
+    desc = ''
+    if symbol in Deprecated:
+        desc += "<warning><para><literal>%s</literal> " % symbol
+
+        note = Deprecated[symbol]
+
+        m = re.search(r'^\s*([0-9\.]+)\s*:?', note)
+        if m:
+            desc += "has been deprecated since version %s and should not be used in newly-written 
code.</para>" % m.group(1)
+        else:
+            desc += "is deprecated and should not be used in newly-written code.</para>"
+
+        note = re.sub(r'^\s*([0-9\.]+)\s*:?\s*', '', note)
+        note = note.strip()
+
+        if note != '':
+            note = ConvertMarkDown(symbol, note)
+            desc += " " + note
+
+        desc += "</warning>\n"
+
+    return desc
+
 
 #############################################################################
 # Function    : MakeConditionDescription
@@ -3374,49 +3107,43 @@ sub MakeDeprecationNote {
 # Arguments   : $symbol - the symbol to try to create the sumary.
 #############################################################################
 
-sub MakeConditionDescription {
-    my ($symbol) = $_[0];
-    my $desc = "";
-
-    if (exists $Deprecated{$symbol}) {
-        if ($desc ne "") {
-            $desc .= "|";
-        }
-
-        if ($Deprecated{$symbol} =~ /^\s*(.*?)\s*$/) {
-                $desc .= "deprecated:$1";
-        } else {
-                $desc .= "deprecated";
-        }
-    }
-
-    if (exists $Since{$symbol}) {
-        if ($desc ne "") {
-            $desc .= "|";
-        }
-
-        if ($Since{$symbol} =~ /^\s*(.*?)\s*$/) {
-                $desc .= "since:$1";
-        } else {
-                $desc .= "since";
-        }
-    }
-
-    if (exists $StabilityLevel{$symbol}) {
-        if ($desc ne "") {
-            $desc .= "|";
-        }
-        $desc .= "stability:".$StabilityLevel{$symbol};
-    }
-
-    if ($desc ne "") {
-        my $cond = $desc;
-        $cond =~ s/\"/&quot;/g;
-        $desc=" condition=\"".$cond."\"";
-        @TRACE@("condition for '$symbol' = '$desc'\n");
-    }
-    return $desc;
-}
+def MakeConditionDescription(symbol):
+    desc = ''
+    if symbol in Deprecated:
+        if desc != '':
+            desc += "|"
+        m = re.search(r'^\s*(.*?)\s*$', Deprecated[symbol])
+        if m:
+            desc += "deprecated:%s" % m.group(1)
+        else:
+            desc += "deprecated"
+
+    if symbol in Since:
+        if desc != '':
+            desc += "|"
+        m = re.search(r'^\s*(.*?)\s*$', Since[symbol])
+        if m:
+            desc += "since:%s" % m.group(1)
+        else:
+            desc += "since"
+
+
+
+    if symbol in StabilityLevel:
+        if desc != '':
+            desc += "|"
+
+        desc += "stability:" + StabilityLevel[symbol]
+
+
+    if desc != '':
+        cond = desc
+        cond = re.sub(r'"', r'&quot', cond)
+        desc = ' condition=\"%s\"' % cond
+        logging.info("condition for '%s' = '%s'\n", symbol, desc)
+
+    return desc
+
 
 #############################################################################
 # Function    : GetHierarchy
@@ -3428,121 +3155,104 @@ sub MakeConditionDescription {
 #               @hierarchy - previous hierarchy
 #############################################################################
 
-sub GetHierarchy {
-    my ($object,$hierarchy_ref) = @_;
-    my @hierarchy = @{$hierarchy_ref};
+def GetHierarchy(gobject, hierarchy_ref):
+    hierarchy = hierarchy_ref
 
     # Find object in the objects array.
-    my $found = 0;
-    my @children = ();
-    my $i;
-    my $level;
-    my $j;
-    for ($i = 0; $i < @Objects; $i++) {
-        if ($found) {
-            if ($ObjectLevels[$i] <= $level) {
-            last;
-        }
-            elsif ($ObjectLevels[$i] == $level + 1) {
-                push (@children, $Objects[$i]);
-            }
-        }
-        elsif ($Objects[$i] eq $object) {
-            $found = 1;
-            $j = $i;
-            $level = $ObjectLevels[$i];
-        }
-    }
-    if (!$found) {
-        return @hierarchy;
-    }
+    found = False
+    children = []
+    level = 0
+    for i in range(len(Objects)):
+        if found:
+            if ObjectLevels[i] <= level:
+                continue
+
+            elif ObjectLevels[i] == level + 1:
+                children.append(Objects[i])
+
+        elif Objects[i] == gobject:
+            found = True
+            j = i
+            level = ObjectLevels[i]
+
+    if found:
+        return hierarchy
 
     # Walk up the hierarchy, pushing ancestors onto the ancestors array.
-    my @ancestors = ();
-    push (@ancestors, $object);
-    @TRACE@("Level: $level\n");
-    while ($level > 1) {
-        $j--;
-        if ($ObjectLevels[$j] < $level) {
-            push (@ancestors, $Objects[$j]);
-            $level = $ObjectLevels[$j];
-            @TRACE@("Level: $level\n");
-        }
-    }
+    ancestors = [gobject]
+    logging.info("Level: %s\n", level)
+    while level > 1:
+        j -= 1
+        if ObjectLevels[j] < level:
+            ancestors.append(Objects[j])
+            level = ObjectLevels[j]
+            logging.info("Level: %s", level)
 
     # Output the ancestors, indented and with links.
-    my $last_index = 0;
-    $level = 1;
-    for ($i = $#ancestors; $i >= 0; $i--) {
-        my $entry_text;
-        my $alt_text;
-        my $ancestor = $ancestors[$i];
-        my $ancestor_id = &CreateValidSGMLID ($ancestor);
-        my $indent = ' ' x ($level * 4);
+    last_index = 0
+    level = 1
+    for i in range(len(ancestors), -1, -1):
+        ancestor = ancestors[i]
+        ancestor_id = common.CreateValidSGMLID(ancestor)
+        indent = ' ' * (level * 4)
         # Don't add a link to the current object, i.e. when i == 0.
-        if ($i > 0) {
-            $entry_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
-            $alt_text = $indent . $ancestor;
-        } else {
-            $entry_text = $indent . $ancestor;
-            $alt_text = $indent . "<link linkend=\"$ancestor_id\">$ancestor</link>";
-        }
-        @TRACE@("Checking for '$entry_text' or '$alt_text'");
+        if i > 0:
+            entry_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
+            alt_text = indent + ancestor
+        else:
+            entry_text = indent + ancestor
+            alt_text = indent + "<link linkend=\"%s\">%s</link>" % (ancestor_id, ancestor)
+
+        logging.info("Checking for '%s' or '%s'", entry_text, alt_text)
         # Check if we already have this object
-        my $index = -1;
-        for ($j = 0; $j <= $#hierarchy; $j++) {
-            if (($hierarchy[$j] eq $entry_text) or ($hierarchy[$j] eq $alt_text)) {
-                $index = $j;
-                last;
-            }
-        }
-        if ($index == -1) {
+        index = -1
+        for j in range(len(hierarchy)):
+            if hierarchy[j] == entry_text or (hierarchy[j] == alt_text):
+                index = j
+                break
+        if index == -1:
             # We have a new entry, find insert position in alphabetical order
-            my $found = 0;
-            for ($j = $last_index; $j <= $#hierarchy; $j++) {
-                if ($hierarchy[$j] !~ m/^${indent}/) {
-                    $last_index = $j;
-                    $found = 1;
-                    last;
-                } elsif ($hierarchy[$j] =~ m/^${indent}[^ ]/) {
-                    my $stripped_text = $hierarchy[$j];
-                    if ($entry_text !~ m/<link linkend/) {
-                        $stripped_text =~ s%<link linkend="[A-Za-z]*">%%;
-                        $stripped_text =~ s%</link>%%;
-                    }
-                    if ($entry_text lt $stripped_text) {
-                        $last_index = $j;
-                        $found = 1;
-                        last;
-                    }
-                }
-            }
+            found = 0
+            for j in range(last_index, len(hierarchy)):
+                if re.search(r'^$%s' % indent, hierarchy[j]):
+                    last_index = j
+                    found = True
+                    break
+                elif re.search(r'^%s[^ ]' % indent, hierarchy[j]):
+                    stripped_text = hierarchy[j]
+                    if r'<link linkend' not in entry_text:
+                        stripped_text = re.sub(r'<link linkend="[A-Za-z]*">', '', stripped_text)
+                        stripped_text = re.sub(r'</link>', '', stripped_text)
+
+                    if entry_text < stripped_text:
+                        last_index = j
+                        found = True
+                        break
+
             # Append to bottom
-            if (!$found) {
-              $last_index = 1 + $#hierarchy;
-            }
-            splice @hierarchy, $last_index, 0, ($entry_text);
-            $last_index++;
-        } else {
+            if not found:
+                last_index = len(hierarchy)
+
+            hierarchy.insert(last_index, entry_text)
+            last_index += 1
+        else:
             # Already have this one, make sure we use the not linked version
-            if ($entry_text !~ m/<link linkend=/) {
-              $hierarchy[$j] = $entry_text;
-            }
+            if re.search(r'<link linkend=', entry_text):
+                hierarchy[j] = entry_text
+
             # Remember index as base insert point
-            $last_index = $index + 1;
-        }
-        $level++;
-    }
+            last_index = index + 1
+
+        level += 1
+
     # Output the children, indented and with links.
-    for ($i = 0; $i <= $#children; $i++) {
-        my $id = &CreateValidSGMLID ($children[$i]);
-        my $indented_text = ' ' x ($level * 4) . "<link linkend=\"$id\">$children[$i]</link>";
-        splice @hierarchy, $last_index, 0, ($indented_text);
-        $last_index++;
-    }
-
-    return @hierarchy;
-}
+    for i in range(len(children)):
+        sid = common.CreateValidSGMLID(children[i])
+        indented_text = ' ' * (level * 4) + "<link linkend=\"%s\">%s</link>" % (sid, children[i])
+        hierarchy.insert(last_index, indented_text)
+        last_index += 1
+    return hierarchy
+
 
 #############################################################################
 # Function    : GetInterfaces
@@ -3551,38 +3261,28 @@ sub GetHierarchy {
 # Arguments   : $object - the GtkObject subclass.
 #############################################################################
 
-sub GetInterfaces {
-    my ($object) = @_;
-    my $text = "";
-    my $i;
+def GetInterfaces(gobject):
+    text = ''
 
     # Find object in the objects array.
-    if (exists($Interfaces{$object})) {
-        my @ifaces = split(' ', $Interfaces{$object});
-        $text = <<EOF;
-<para>
-$object implements
-EOF
-        for ($i = 0; $i <= $#ifaces; $i++) {
-            my $id = &CreateValidSGMLID ($ifaces[$i]);
-            $text .= " <link linkend=\"$id\">$ifaces[$i]</link>";
-            if ($i < $#ifaces - 1) {
-                $text .= ', ';
-            }
-            elsif ($i < $#ifaces) {
-                $text .= ' and ';
-            }
-            else {
-                $text .= '.';
-            }
-        }
-        $text .= <<EOF;
-</para>
-EOF
-    }
-
-    return $text;
-}
+    if gobject in Interfaces:
+        ifaces = Interfaces[gobject].split()
+        text = '''<para>
+% implements
+''' % gobject
+        for i in range(len(ifaces)):
+            sid = common.CreateValidSGMLID(ifaces[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, ifaces[i])
+            if i < len(ifaces) - 1:
+                text += ', '
+            elif i < len(ifaces):
+                text += ' and '
+            else:
+                text += '.'
+        text += '''</para>
+'''
+    return text
+
 
 #############################################################################
 # Function    : GetImplementations
@@ -3591,41 +3291,34 @@ EOF
 # Arguments   : $object - the GtkObject subclass.
 #############################################################################
 
-sub GetImplementations {
-    my ($object) = @_;
-    my @impls = ();
-    my $text = "";
-    my $i;
-    foreach my $key (keys %Interfaces) {
-        if ($Interfaces{$key} =~ /\b$object\b/) {
-            push (@impls, $key);
-        }
-    }
-    if ($#impls >= 0) {
-        @impls = sort @impls;
-        $text = <<EOF;
-<para>
-$object is implemented by
-EOF
-        for ($i = 0; $i <= $#impls; $i++) {
-            my $id = &CreateValidSGMLID ($impls[$i]);
-            $text .= " <link linkend=\"$id\">$impls[$i]</link>";
-            if ($i < $#impls - 1) {
-                $text .= ', ';
-            }
-            elsif ($i < $#impls) {
-                $text .= ' and ';
-            }
-            else {
-                $text .= '.';
-            }
-        }
-        $text .= <<EOF;
-</para>
-EOF
-    }
-    return $text;
-}
+def GetImplementations(gobject):
+    impls = []
+    text = ''
+
+    for key in Interfaces:
+        if re.search(r'\b%s\b' % gobject, Interfaces[key]):
+            impls.append(key)
+
+    if len(impls) > 0:
+        impls.sort()
+        text = ''''<para>
+%s is implemented by
+''' % gobject
+        for i in range(len(impls)):
+            sid = common.CreateValidSGMLID(impls[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, impls[i])
+            if i < len(impls) - 1:
+                text += ', '
+
+            elif i < len(impls):
+                text += ' and '
+
+            else:
+                text += '.'
+        text += '''</para>
+'''
+    return text
+
 
 
 #############################################################################
@@ -3635,36 +3328,30 @@ EOF
 # Arguments   : $iface - the interface.
 #############################################################################
 
-sub GetPrerequisites {
-    my ($iface) = @_;
-    my $text = "";
-    my $i;
+def GetPrerequisites(iface):
+    text = ''
 
-    if (exists($Prerequisites{$iface})) {
-        $text = <<EOF;
+    if iface in Prerequisites:
+        text = '''
 <para>
-$iface requires
-EOF
-        my @prereqs = split(' ', $Prerequisites{$iface});
-        for ($i = 0; $i <= $#prereqs; $i++) {
-            my $id = &CreateValidSGMLID ($prereqs[$i]);
-            $text .= " <link linkend=\"$id\">$prereqs[$i]</link>";
-            if ($i < $#prereqs - 1) {
-                $text .= ', ';
-            }
-            elsif ($i < $#prereqs) {
-                $text .= ' and ';
-            }
-            else {
-                $text .= '.';
-            }
-        }
-        $text .= <<EOF;
-</para>
-EOF
-    }
-    return $text;
-}
+%s requires
+''' % iface
+        prereqs = Prerequisites[iface].split()
+        for i in range(len(prereqs)):
+            sid = common.CreateValidSGMLID(prereqs[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, prereqs[i])
+            if i < len(prereqs) - 1:
+                text += ', '
+            elif i < len(prereqs):
+                text += ' and '
+            else:
+                text += '.'
+
+
+        text += '''</para>
+'''
+    return text
+
 
 #############################################################################
 # Function    : GetDerived
@@ -3673,42 +3360,31 @@ EOF
 # Arguments   : $iface - the interface.
 #############################################################################
 
-sub GetDerived {
-    my ($iface) = @_;
-    my $text = "";
-    my $i;
-
-    my @derived = ();
-    foreach my $key (keys %Prerequisites) {
-        if ($Prerequisites{$key} =~ /\b$iface\b/) {
-            push (@derived, $key);
-        }
-    }
-    if ($#derived >= 0) {
-        @derived = sort @derived;
-        $text = <<EOF;
-<para>
-$iface is required by
-EOF
-        for ($i = 0; $i <= $#derived; $i++) {
-            my $id = &CreateValidSGMLID ($derived[$i]);
-            $text .= " <link linkend=\"$id\">$derived[$i]</link>";
-            if ($i < $#derived - 1) {
-                $text .= ', ';
-            }
-            elsif ($i < $#derived) {
-                $text .= ' and ';
-            }
-            else {
-                $text .= '.';
-            }
-        }
-        $text .= <<EOF;
-</para>
-EOF
-    }
-    return $text;
-}
+def GetDerived(iface):
+    text = ''
+
+    derived = []
+    for key in Prerequisites:
+        if re.search(r'\b%s\b' % iface, Prerequisites[key]):
+            derived.append(key)
+    if len(derived) > 0:
+        derived.sort()
+        text = '''<para>
+%s is required by
+''' % iface
+        for i in range(len(derived)):
+            sid = common.CreateValidSGMLID(derived[i])
+            text += " <link linkend=\"%s\">%s</link>" % (sid, derived[i])
+            if i < len(derived) - 1:
+                text += ', '
+            elif i < len(derived):
+                text += ' and '
+            else:
+                text += '.'
+        text += '''</para>
+'''
+    return text
+
 
 
 #############################################################################
@@ -3718,154 +3394,143 @@ EOF
 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
 #############################################################################
 
-sub GetSignals {
-    my ($object) = @_;
-    my $synop = "";
-    my $desc = "";
-
-    my $i;
-    for ($i = 0; $i <= $#SignalObjects; $i++) {
-        if ($SignalObjects[$i] eq $object) {
-            @TRACE@("Found signal: $SignalNames[$i]\n");
-            my $name = $SignalNames[$i];
-            my $symbol = "${object}::${name}";
-            my $id = &CreateValidSGMLID ("$object-$name");
-
-            $desc .= "<refsect2 id=\"$id\" role=\"signal\"><title>The <literal>“$name”</literal> 
signal</title>\n";
-            $desc .= MakeIndexterms($symbol, $id);
-            $desc .= "\n";
-            $desc .= OutputSymbolExtraLinks($symbol);
-
-            $desc .= "<programlisting language=\"C\">";
-
-            $SignalReturns[$i] =~ m/\s*(const\s+)?(\w+)\s*(\**)/;
-            my $type_modifier = defined($1) ? $1 : "";
-            my $type = $2;
-            my $pointer = $3;
-            my $xref = &MakeXRef ($type, &tagify($type, "returnvalue"));
-
-            my $ret_type_output = "$type_modifier$xref$pointer";
-            my $callback_name = "user_function";
-            $desc  .= "${ret_type_output}\n${callback_name} (";
-
-            my $indentation = ' ' x (length($callback_name) + 2);
-            my $pad = $indentation;
-
-            my $sourceparams = $SourceSymbolParams{$symbol};
-            my @params = split ("\n", $SignalPrototypes[$i]);
-            my $j;
-            my $l;
-            my $type_len = length("gpointer");
-            my $name_len = length("user_data");
+def GetSignals(gobject):
+    synop = ''
+    desc = ''
+
+    for i in range(len(SignalObjects)):
+        if SignalObjects[i] == gobject:
+            logging.info("Found signal: %s\n", SignalNames[i])
+            name = SignalNames[i]
+            symbol = '%s::%s' % (gobject, name)
+            sid = common.CreateValidSGMLID('%s-%s' % (gobject, name))
+
+            desc += "<refsect2 id=\"%s\" role=\"signal\"><title>The <literal>“%s”</literal> 
signal</title>\n" % (sid, name)
+            desc += MakeIndexterms(symbol, sid)
+            desc += "\n"
+            desc += OutputSymbolExtraLinks(symbol)
+
+            desc += "<programlisting language=\"C\">"
+
+            m = re.search(r'\s*(const\s+)?(\w+)\s*(\**)', SignalReturns[i])
+            type_modifier = m.group(1) if m.group(1) else ''
+            gtype = m.group(2)
+            pointer = m.group(3)
+            xref = MakeXRef(gtype, tagify(gtype, "returnvalue"))
+
+            ret_type_output = '%s%s%s' % (type_modifier, xref, pointer)
+            callback_name = "user_function"
+            desc += '%s\n%s (' % (ret_type_output, callback_name)
+
+            indentation = ' ' * len(callback_name) + 2
+            pad = indentation
+
+            sourceparams = SourceSymbolParams[symbol]
+            params = SignalPrototypes[i].split('\n')
+            type_len = len("gpointer")
+            name_len = len("user_data")
             # do two passes, the first one is to calculate padding
-            for ($l = 0; $l < 2; $l++) {
-                for ($j = 0; $j <= $#params; $j++) {
-                    my $param_name;
+            for l in range(2):
+                for j in range(len(params)):
+                    param_name = None
                     # allow alphanumerics, '_', '[' & ']' in param names
-                    if ($params[$j] =~ m/^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$/) {
-                        $type = $1;
-                        $pointer = $2;
-                        if (defined($sourceparams)) {
-                            $param_name = $$sourceparams[$PARAM_FIELD_COUNT * $j];
-                        }
-                        else {
-                            $param_name = $3;
-                        }
-                        if (!defined($param_name)) {
-                            $param_name = "arg$j";
-                        }
-                        if ($l == 0) {
-                            if (length($type) + length($pointer) > $type_len) {
-                                $type_len = length($type) + length($pointer);
-                            }
-                            if (length($param_name) > $name_len) {
-                                $name_len = length($param_name);
-                            }
-                        }
-                        else {
-                            $xref = &MakeXRef ($type, &tagify($type, "type"));
-                            $pad = ' ' x ($type_len - length($type) - length($pointer));
-                            $desc .= "$xref$pad $pointer${param_name},\n";
-                            $desc .= $indentation;
-                        }
-                    } else {
-                        &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                             "Can't parse arg: $params[$j]\nArgs:$SignalPrototypes[$i]");
-                    }
-                }
-            }
-            $xref = &MakeXRef ("gpointer", &tagify("gpointer", "type"));
-            $pad = ' ' x ($type_len - length("gpointer"));
-            $desc  .= "$xref$pad user_data)";
-            $desc  .= "</programlisting>\n";
-
-            my $flags = $SignalFlags[$i];
-            my $flags_string = "";
-
-            if (defined ($flags)) {
-              if ($flags =~ m/f/) {
-                $flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>";
-              }
-              elsif ($flags =~ m/l/) {
-                $flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>";
-              }
-              elsif ($flags =~ m/c/) {
-                $flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>";
-                $flags_string = "Cleanup";
-              }
-              if ($flags =~ m/r/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>";
-              }
-              if ($flags =~ m/d/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>";
-              }
-              if ($flags =~ m/a/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>";
-              }
-              if ($flags =~ m/h/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>";
-              }
-            }
-
-            $synop .= "<row><entry role=\"signal_type\">${ret_type_output}</entry><entry 
role=\"signal_name\"><link linkend=\"$id\">${name}</link></entry><entry 
role=\"signal_flags\">${flags_string}</entry></row>\n";
-
-            my $parameters = &OutputParamDescriptions ("SIGNAL", $symbol);
-
-            $AllSymbols{$symbol} = 1;
-            if (defined ($SymbolDocs{$symbol})) {
-                my $symbol_docs = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-
-                $desc .= $symbol_docs;
-
-                if (!IsEmptyDoc($SymbolDocs{$symbol})) {
-                    $AllDocumentedSymbols{$symbol} = 1;
-                }
-            }
-            if (defined ($SymbolAnnotations{$symbol})) {
-                my $param_desc = $SymbolAnnotations{$symbol};
-                my $param_annotations = "";
-                ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
-                if ($param_annotations ne "") {
-                    $desc .= "\n<para>$param_annotations</para>";
-                }
-            }
-            $desc .= &MakeDeprecationNote($symbol);
-
-            $desc .= $parameters;
-            if ($flags_string) {
-                $desc  .= "<para>Flags: $flags_string</para>\n";
-            }
-            $desc .= OutputSymbolTraits ($symbol);
-            $desc .= "</refsect2>";
-        }
-    }
-    return ($synop, $desc);
-}
+                    m = re.search(r'^\s*(\w+)\s*(\**)\s*([\w\[\]]+)\s*$', params[j])
+                    if m:
+                        gtype = m.group(1)
+                        pointer = m.group(2)
+                        if sourceparams:
+                            param_name = sourceparams[PARAM_FIELD_COUNT * j]
+
+                        else:
+                            param_name = m.group(3)
+
+                        if not param_name:
+                            param_name = "arg%d" % j
+
+                        if l == 0:
+                            if len(gtype) + len(pointer) > type_len:
+                                type_len = len(gtype) + len(pointer)
+                            if len(param_name) > name_len:
+                                name_len = len(param_name)
+                        else:
+                            xref = MakeXRef(gtype, tagify(gtype, "type"))
+                            pad = ' ' * (type_len - len(type) - len(pointer))
+                            desc += '%s%s %s%s,\n' % (xref, pad, pointer, param_name)
+                            desc += indentation
+
+                    else:
+                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                          "Can't parse arg: %s\nArgs:%s" % (params[j], SignalPrototypes[i]))
+
+            xref = MakeXRef("gpointer", tagify("gpointer", "type"))
+            pad = ' ' * (type_len - len("gpointer"))
+            desc += '%s%s user data)' % (xref, pad)
+            desc += "</programlisting>\n"
+
+            flags = SignalFlags[i]
+            flags_string = ''
+
+            if flags:
+                if 'f' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-FIRST:CAPS\">Run First</link>"
+
+                elif 'l' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-LAST:CAPS\">Run Last</link>"
+
+                elif 'c' in flags:
+                    flags_string = "<link linkend=\"G-SIGNAL-RUN-CLEANUP:CAPS\">Cleanup</link>"
+                    flags_string = "Cleanup"
 
+                if 'r' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-NO-RECURSE:CAPS\">No Recursion</link>"
+
+                if 'd' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-DETAILED:CAPS\">Has Details</link>"
+
+                if 'a' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-ACTION:CAPS\">Action</link>"
+
+                if 'h' in flags:
+                    if flags_string:
+                        flags_string += " / "
+                    flags_string = "<link linkend=\"G-SIGNAL-NO-HOOKS:CAPS\">No Hooks</link>"
+
+            synop += "<row><entry role=\"signal_type\">%s</entry><entry role=\"signal_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"signal_flags\">%s</entry></row>\n" % (ret_type_output, sid, 
name, flags_string)
+
+            parameters = OutputParamDescriptions("SIGNAL", symbol)
+
+            AllSymbols[symbol] = 1
+            if SymbolDocs[symbol]:
+                symbol_docs = ConvertMarkDown(symbol, SymbolDocs[symbol])
+
+                desc += symbol_docs
+
+                if not IsEmptyDoc(SymbolDocs[symbol]):
+                    AllDocumentedSymbols[symbol] = 1
+
+            if SymbolAnnotations[symbol]:
+                param_desc = SymbolAnnotations[symbol]
+                param_annotations = ''
+                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+                if param_annotations != '':
+                    desc += "\n<para>%s</para>" % param_annotations
+
+            desc += MakeDeprecationNote(symbol)
+
+            desc += parameters
+            if flags_string:
+                desc += "<para>Flags: %s</para>\n" % flags_string
+
+            desc += OutputSymbolTraits(symbol)
+            desc += "</refsect2>"
+
+    return (synop, desc)
 
 #############################################################################
 # Function    : GetArgs
@@ -3874,147 +3539,139 @@ sub GetSignals {
 # Arguments   : $object - the GtkObject subclass, e.g. 'GtkButton'.
 #############################################################################
 
-sub GetArgs {
-    my ($object) = @_;
-    my $synop = "";
-    my $desc = "";
-    my $child_synop = "";
-    my $child_desc = "";
-    my $style_synop = "";
-    my $style_desc = "";
-
-    my $i;
-    for ($i = 0; $i <= $#ArgObjects; $i++) {
-        if ($ArgObjects[$i] eq $object) {
-            @TRACE@("Found arg: $ArgNames[$i]\n");
-            my $name = $ArgNames[$i];
-            my $flags = $ArgFlags[$i];
-            my $flags_string = "";
-            my $kind = "";
-            my $id_sep = "";
-
-            if ($flags =~ m/c/) {
-                $kind = "child property";
-                $id_sep = "c-";
-            }
-            elsif ($flags =~ m/s/) {
-                $kind = "style property";
-                $id_sep = "s-";
-            }
-            else {
-                $kind = "property";
-            }
+def GetArgs(gobject):
+    synop = ''
+    desc = ''
+    child_synop = ''
+    child_desc = ''
+    style_synop = ''
+    style_desc = ''
+
+    for i in range(len(ArgObjects)):
+        if ArgObjects[i] == gobject:
+            logging.info("Found arg: %s", ArgNames[i])
+            name = ArgNames[i]
+            flags = ArgFlags[i]
+            flags_string = ''
+            kind = ''
+            id_sep = ''
+
+            if 'c' in flags:
+                kind = "child property"
+                id_sep = "c-"
+            elif 's' in flags:
+                kind = "style property"
+                id_sep = "s-"
+            else:
+                kind = "property"
+
 
             # Remember only one colon so we don't clash with signals.
-            my $symbol = "${object}:${name}";
+            symbol = '%s:%s' % (gobject, name)
             # use two dashes and ev. an extra separator here for the same reason.
-            my $id = &CreateValidSGMLID ("$object--$id_sep$name");
-
-            my $type = $ArgTypes[$i];
-            my $type_output;
-            my $range = $ArgRanges[$i];
-            my $range_output = CreateValidSGML ($range);
-            my $default = $ArgDefaults[$i];
-            my $default_output = CreateValidSGML ($default);
-
-            if ($type eq "GtkString") {
-                $type = "char&#160;*";
-            }
-            if ($type eq "GtkSignal") {
-                $type = "GtkSignalFunc, gpointer";
-                $type_output = &MakeXRef ("GtkSignalFunc") . ", "
-                    . &MakeXRef ("gpointer");
-            } elsif ($type =~ m/^(\w+)\*$/) {
-                $type_output = &MakeXRef ($1, &tagify($1, "type")) . "&#160;*";
-            } else {
-                $type_output = &MakeXRef ($type, &tagify($type, "type"));
-            }
-
-            if ($flags =~ m/r/) {
-                $flags_string = "Read";
-            }
-            if ($flags =~ m/w/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string .= "Write";
-            }
-            if ($flags =~ m/x/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string .= "Construct";
-            }
-            if ($flags =~ m/X/) {
-                if ($flags_string) { $flags_string .= " / "; }
-                $flags_string .= "Construct Only";
-            }
-
-            $AllSymbols{$symbol} = 1;
-            my $blurb = "";
-            if (defined($SymbolDocs{$symbol}) &&
-                !IsEmptyDoc($SymbolDocs{$symbol})) {
-                $blurb = &ConvertMarkDown($symbol, $SymbolDocs{$symbol});
-                @TRACE@(".. [$SymbolDocs{$symbol}][$blurb]\n");
-                $AllDocumentedSymbols{$symbol} = 1;
-            }
-            else {
-                if ($ArgBlurbs[$i] ne "") {
-                    $blurb = "<para>" . &CreateValidSGML ($ArgBlurbs[$i]) . "</para>";
-                    $AllDocumentedSymbols{$symbol} = 1;
-                } else {
+            sid = common.CreateValidSGMLID('%s--%s%s' % (gobject, id_sep, name))
+
+            atype = ArgTypes[i]
+            type_output = None
+            arange = ArgRanges[i]
+            range_output = CreateValidSGML(arange)
+            default = ArgDefaults[i]
+            default_output = CreateValidSGML(default)
+
+            if type == "GtkString":
+                type = "char&#160;*"
+
+            if type == "GtkSignal":
+                type = "GtkSignalFunc, gpointer"
+                type_output = MakeXRef("GtkSignalFunc") + ", " + MakeXRef("gpointer")
+            elif re.search(r'^(\w+)\*$', type):
+                m = re.search(r'^(\w+)\*$', type)
+                type_output = MakeXRef(m.group(1), tagify(m.group(1), "type")) + "&#160;*"
+            else:
+                type_output = MakeXRef(atype, tagify(atype, "type"))
+
+            if 'r' in flags:
+                flags_string = "Read"
+
+            if 'w' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Write"
+
+            if 'x' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Construct"
+
+            if 'X' in flags:
+                if flags_string:
+                    flags_string += " / "
+                flags_string += "Construct Only"
+
+
+            AllSymbols[symbol] = 1
+            blurb = ''
+            if symbol in SymbolDocs and not IsEmptyDoc(SymbolDocs[symbol]):
+                blurb = ConvertMarkDown(symbol, SymbolDocs[symbol])
+                logging.info(".. [%s][%s]\n", SymbolDocs[symbol], blurb)
+                AllDocumentedSymbols[symbol] = 1
+
+            else:
+                if ArgBlurbs[i] != '':
+                    blurb = "<para>" + CreateValidSGML(ArgBlurbs[i]) + "</para>"
+                    AllDocumentedSymbols[symbol] = 1
+                else:
                     # FIXME: print a warning?
-                    @TRACE@(".. no description\n");
-                }
-            }
-
-            my $pad1 = "";
-            if (length ($name) < 24) {
-              $pad1 = " " x (24 - length ($name));
-            }
-
-            my $arg_synop = "<row><entry role=\"property_type\">$type_output</entry><entry 
role=\"property_name\"><link linkend=\"$id\">$name</link></entry><entry 
role=\"property_flags\">$flags_string</entry></row>\n";
-            my $arg_desc = "<refsect2 id=\"$id\" role=\"property\"><title>The <literal>“$name”</literal> 
$kind</title>\n";
-            $arg_desc .= MakeIndexterms($symbol, $id);
-            $arg_desc .= "\n";
-            $arg_desc .= OutputSymbolExtraLinks($symbol);
-
-            $arg_desc .= "<programlisting>  “$name”$pad1 $type_output</programlisting>\n";
-            $arg_desc .= $blurb;
-            if (defined ($SymbolAnnotations{$symbol})) {
-                my $param_desc = $SymbolAnnotations{$symbol};
-                my $param_annotations = "";
-                ($param_desc,$param_annotations) = &ExpandAnnotation($symbol, $param_desc);
-                if ($param_annotations ne "") {
-                    $arg_desc .= "\n<para>$param_annotations</para>";
-                }
-            }
-            $arg_desc .= &MakeDeprecationNote($symbol);
-
-            if ($flags_string) {
-              $arg_desc  .= "<para>Flags: $flags_string</para>\n";
-            }
-            if ($range ne "") {
-                $arg_desc .= "<para>Allowed values: $range_output</para>\n";
-            }
-            if ($default ne "") {
-                $arg_desc .= "<para>Default value: $default_output</para>\n";
-            }
-            $arg_desc .= OutputSymbolTraits ($symbol);
-            $arg_desc .= "</refsect2>\n";
-
-            if ($flags =~ m/c/) {
-                $child_synop .= $arg_synop;
-                $child_desc .= $arg_desc;
-            }
-            elsif ($flags =~ m/s/) {
-                $style_synop .= $arg_synop;
-                $style_desc .= $arg_desc;
-            }
-            else {
-                $synop .= $arg_synop;
-                $desc .= $arg_desc;
-            }
-        }
-    }
-    return ($synop, $child_synop, $style_synop, $desc, $child_desc, $style_desc);
-}
+                    logging.info(".. no description\n")
+
+            pad1 = ''
+            if len(name) < 24:
+                pad1 = " " * (24 - len(name))
+
+
+            arg_synop = "<row><entry role=\"property_type\">%s</entry><entry role=\"property_name\"><link 
linkend=\"%s\">%s</link></entry><entry role=\"property_flags\">%s</entry></row>\n" % (type_output, sid, name, 
flags_string)
+            arg_desc = "<refsect2 id=\"%s\" role=\"property\"><title>The <literal>“%s”</literal> 
%s</title>\n" % (sid, name, kind)
+            arg_desc += MakeIndexterms(symbol, sid)
+            arg_desc += "\n"
+            arg_desc += OutputSymbolExtraLinks(symbol)
+
+            arg_desc += "<programlisting>  “%s”%s %s</programlisting>\n" % (name, pad1, type_output)
+            arg_desc += blurb
+            if symbol in SymbolAnnotations:
+                param_desc = SymbolAnnotations[symbol]
+                param_annotations = ''
+                (param_desc, param_annotations) = ExpandAnnotation(symbol, param_desc)
+                if param_annotations != '':
+                    arg_desc += "\n<para>%s</para>" % param_annotations
+
+            arg_desc += MakeDeprecationNote(symbol)
+
+            if flags_string:
+                arg_desc += "<para>Flags: %s</para>\n" % flags_string
+
+            if range != '':
+                arg_desc += "<para>Allowed values: %s</para>\n" % range_output
+
+            if default != '':
+                arg_desc += "<para>Default value: %s</para>\n" % default_output
+
+            arg_desc += OutputSymbolTraits(symbol)
+            arg_desc += "</refsect2>\n"
+
+            if 'c' in flags:
+                child_synop += arg_synop
+                child_desc += arg_desc
+
+            elif 's' in flags:
+                style_synop += arg_synop
+                style_desc += arg_desc
+
+            else:
+                synop += arg_synop
+                desc += arg_desc
+
+    return (synop, child_synop, style_synop, desc, child_desc, style_desc)
+
 
 
 #############################################################################
@@ -4033,54 +3690,43 @@ sub GetArgs {
 # Arguments   : $source_dir - the directory to scan.
 #############m###############################################################
 
-sub ReadSourceDocumentation {
-    my ($source_dir) = @_;
-    my ($file, $dir, @suffix_list, $suffix);
+def ReadSourceDocumentation(source_dir):
 
     # prepend entries from @SOURCE_DIR
-    for my $dir (@SOURCE_DIRS) {
+    for sdir in SOURCE_DIRS:
         # Check if the filename is in the ignore list.
-        if ($source_dir =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
-            @TRACE@("Skipping source directory: $source_dir");
-            return;
-        } else {
-            @TRACE@("No match for: ".($1 || $source_dir));
-        }
-    }
+        m1 = re.search(r'^%s/(.*)$' % re.escape(sdir), source_dir)
+        if m1:
+            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
+            if m2:
+                logging.info("Skipping source directory: %s", source_dir)
+                return
+        else:
+            logging.info("No match for: %s", (m2.group(1) or source_dir))
 
-    @TRACE@("Scanning source directory: $source_dir");
+    logging.info("Scanning source directory: %s", source_dir)
 
     # This array holds any subdirectories found.
-    my (@subdirs) = ();
-
-    @suffix_list = split (/,/, $SOURCE_SUFFIXES);
-
-    opendir (SRCDIR, $source_dir)
-        || die "Can't open source directory $source_dir: $!";
-
-    foreach $file (readdir (SRCDIR)) {
-      if ($file =~ /^\./) {
-        next;
-      } elsif (-d "$source_dir/$file") {
-        push (@subdirs, $file);
-      } elsif (@suffix_list) {
-        foreach $suffix (@suffix_list) {
-          if ($file =~ m/\.\Q${suffix}\E$/) {
-            &ScanSourceFile ("$source_dir/$file");
-          }
-        }
-      } elsif ($file =~ m/\.[ch]$/) {
-        &ScanSourceFile ("$source_dir/$file");
-      }
-    }
-    closedir (SRCDIR);
+    subdirs = []
+
+    suffix_list = SOURCE_SUFFIXES.split(',')
+
+    for ifile in os.listdir(source_dir):
+        fname = os.path.join(source_dir, ifile)
+        if ifile.startswith('.'):
+            continue
+        elif os.path.isdir(fname):
+            subdirs.append(ifile)
+        elif suffix_list:
+            for suffix in suffix_list:
+                if re.search(r'\.%s$' % re.escape(suffix), ifile):
+                    ScanSourceFile(fname)
+        elif re.search(r'\.[ch]$', ifile):
+            ScanSourceFile(fname)
 
     # Now recursively scan the subdirectories.
-    foreach $dir (@subdirs) {
-        &ReadSourceDocumentation ("$source_dir/$dir");
-    }
-}
-
+    for sdir in subdirs:
+        ReadSourceDocumentation(os.path.join(source_dir, sdir))
 
 #############################################################################
 # Function    : ScanSourceFile
@@ -4092,350 +3738,353 @@ sub ReadSourceDocumentation {
 # Arguments   : $file - the file to scan.
 #############################################################################
 
-sub ScanSourceFile {
-    my ($file) = @_;
-    my $basename;
+def ScanSourceFile(ifile):
 
     # prepend entries from @SOURCE_DIR
-    for my $dir (@SOURCE_DIRS) {
+    for idir in SOURCE_DIRS:
         # Check if the filename is in the ignore list.
-        if ($file =~ m%^\Q$dir\E/(.*)$% and $IGNORE_FILES =~ m/(\s|^)\Q$1\E(\s|$)/) {
-            @TRACE@("Skipping source file: $file");
-            return;
-        }
-    }
-
-    if ($file =~ m/^.*[\/\\]([^\/\\]*)$/) {
-        $basename = $1;
-    } else {
-        &LogWarning ($file, 1, "Can't find basename for this filename.");
-        $basename = $file;
-    }
+        m1 = re.search(r'^%s/(.*)$' % re.escape(idir), ifile)
+        if m1:
+            m2 = re.search(r'(\s|^)%s(\s|$)' % re.escape(m1.group(1)), IGNORE_FILES)
+            if m2:
+                logging.info("Skipping source file: ", ifile)
+                return
+
+    m = re.search(r'^.*[\/\\]([^\/\\]*)$', ifile)
+    if m:
+        basename = m.group(1)
+    else:
+        common.LogWarning(ifile, 1, "Can't find basename for this filename.")
+        basename = ifile
+
 
     # Check if the basename is in the list of files to ignore.
-    if ($IGNORE_FILES =~ m/(\s|^)\Q${basename}\E(\s|$)/) {
-        @TRACE@("Skipping source file: $file");
-        return;
-    }
-
-    @TRACE@("Scanning source file: $file");
-
-    open (SRCFILE, $file)
-        || die "Can't open $file: $!";
-    my $in_comment_block = 0;
-    my $symbol;
-    my $in_part = "";
-    my ($description, $return_desc);
-    my ($since_desc, $stability_desc, $deprecated_desc);
-    my $current_param;
-    my @params;
-    while (<SRCFILE>) {
+    if re.search(r'(\s|^)%s(\s|$)' % re.escape(basename), IGNORE_FILES):
+        logging.info("Skipping source file: %s", ifile)
+        return
+
+
+    logging.info("Scanning source file: %s", ifile)
+
+    SRCFILE = open(ifile)
+    in_comment_block = False
+    symbol = None
+    in_part = ''
+    description = ''
+    return_desc = ''
+    since_desc = stability_desc = deprecated_desc = ''
+    current_param = None
+    params = []
+    line_number = 0
+    for line in SRCFILE:
+        line_number += 1
         # Look for the start of a comment block.
-        if (!$in_comment_block) {
-            if (m%^\s*/\*.*\*/%) {
+        if not in_comment_block:
+            if re.search(r'^\s*/\*.*\*/', line):
                 #one-line comment - not gtkdoc
-            } elsif (m%^\s*/\*\*\s%) {
-                @TRACE@("Found comment block start\n");
+                pass
+            elif re.search(r'^\s*/\*\*\s', line):
+                logging.info("Found comment block start\n")
 
-                $in_comment_block = 1;
+                in_comment_block = True
 
                 # Reset all the symbol data.
-                $symbol = "";
-                $in_part = "";
-                $description = "";
-                $return_desc = "";
-                $since_desc = "";
-                $deprecated_desc = "";
-                $stability_desc = "";
-                $current_param = -1;
-                @params = ();
-            }
-            next;
-        }
+                symbol = ''
+                in_part = ''
+                description = ''
+                return_desc = ''
+                since_desc = ''
+                deprecated_desc = ''
+                stability_desc = ''
+                current_param = -1
+                params = []
+
+            continue
 
         # We're in a comment block. Check if we've found the end of it.
-        if (m%^\s*\*+/%) {
-            if (!$symbol) {
+        if re.search(r'^\s*\*+/', line):
+            if symbol:
                 # maybe its not even meant to be a gtk-doc comment?
-                &LogWarning ($file, $., "Symbol name not found at the start of the comment block.");
-            } else {
+                common.LogWarning(ifile, line_number, "Symbol name not found at the start of the comment 
block.")
+            else:
                 # Add the return value description onto the end of the params.
-                if ($return_desc) {
+                if return_desc:
                     # TODO(ensonic): check for duplicated Return docs
-                    # &LogWarning ($file, $., "Multiple Returns for $symbol.");
-                    push (@params, "Returns");
-                    push (@params, $return_desc);
-                }
+                    # &common.LogWarning($file, line_number, "Multiple Returns for $symbol.")
+                    params.append("Returns")
+                    params.append(return_desc)
+
                 # Convert special characters
-                $description = &ConvertSGMLChars ($symbol, $description);
-                my $k;
-                for ($k = 1; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
-                    $params[$k] = &ConvertSGMLChars ($symbol, $params[$k]);
-                }
+                description = ConvertSGMLChars(symbol, description)
+                for k in range(1, len(params), PARAM_FIELD_COUNT):
+                    params[k] = ConvertSGMLChars(symbol, params[k])
+
 
                 # Handle Section docs
-                if ($symbol =~ m/SECTION:\s*(.*)/) {
-                    my $real_symbol=$1;
-                    my $key;
+                m = re.search(r'SECTION:\s*(.*)', symbol)
+                m2 = re.search(r'PROGRAM:\s*(.*)', symbol)
+                if m:
+                    real_symbol = m.group(1)
+                    long_descr = os.path.join(TMPL_DIR, real_symbol + ":Long_Description")
+
+                    if len(KnownSymbols) > 0:
+                        if long_descr in KnownSymbols or KnownSymbols[long_descr] != 1:
+                            common.LogWarning(ifile, line_number, "Section %s is not defined in the 
%s-sections.txt file." % (real_symbol, MODULE))
+
+                    logging.info("SECTION DOCS found in source for : '%s'", real_symbol)
+                    for k in range(0, len(params), PARAM_FIELD_COUNT):
+                        logging.info("   '" + params[k] + "'\n")
+                        params[k] = params[k].lower()
+                        key = None
+                        if params[k] == "short_description":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Short_Description")
+                        elif params[k] == "see_also":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":See_Also")
+                        elif params[k] == "title":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Title")
+                        elif params[k] == "stability":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Stability_Level")
+                        elif params[k] == "section_id":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Section_Id")
+                        elif params[k] == "include":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Include")
+                        elif params[k] == "image":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Image")
+
+                        if key:
+                            SourceSymbolDocs[key] = params[k+1]
+                            SourceSymbolSourceFile[key] = ifile
+                            SourceSymbolSourceLine[key] = line_number
+
+
+                    SourceSymbolDocs[long_descr] = description
+                    SourceSymbolSourceFile[long_descr] = ifile
+                    SourceSymbolSourceLine[long_descr] = line_number
+                    #$SourceSymbolTypes{$symbol} = "SECTION"
+                elif m2:
+                    real_symbol = m2.group(1)
+                    key = None
+                    section_id = None
+
+                    logging.info("PROGRAM DOCS found in source for '%s'", real_symbol)
+                    for k in range(0, len(params), PARAM_FIELD_COUNT):
+                        logging.info("   '" + params[k] + "'\n")
+                        params[k] = params[k].lower()
+                        key = None
+
+                        if params[k] == "short_description":
+                            key = os.path.join(TMPL_DIR, real_symbol, ":Short_Description")
+                        elif params[k] == "see_also":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":See_Also")
+                        elif params[k] == "section_id":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Section_Id")
+                        elif params[k] == "synopsis":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Synopsis")
+                        elif params[k] == "returns":
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Returns")
+                        elif re.search(r'^(-.*)', params[k]):
+                            m4 = re.search(r'^(-.*)', params[k])
+                            key = os.path.join(TMPL_DIR, real_symbol + ":Options")
+                            if key in SourceSymbolDocs:
+                                opts = SourceSymbolDocs[key]
+                            else:
+                                opts = []
+
+                            opts.append(m4.group(1))
+                            opts.append(params[k+1])
+
+                            SourceSymbolDocs[key] = opts
+                            continue
+
+                        if key:
+                            SourceSymbolDocs[key] = params[k+1]
+                            SourceSymbolSourceFile[key] = ifile
+                            SourceSymbolSourceLine[key] = line_number
+
+
+                    long_descr = os.path.join(TMPL_DIR, real_symbol + ":Long_Description")
+                    SourceSymbolDocs[long_descr] = description
+                    SourceSymbolSourceFile[long_descr] = ifile
+                    SourceSymbolSourceLine[long_descr] = line_number
+
+                    section_id = SourceSymbolDocs[os.path.join(TMPL_DIR, real_symbol + ":Section_Id")]
+                    if section_id and not re.search(r'^\s*$', section_id):
+                        # Remove trailing blanks and use as is
+                        section_id = section_id.rstrip()
+                    else:
+                        section_id = common.CreateValidSGMLID('%s-%s' % (MODULE, real_symbol))
+                    OutputProgramDBFile(real_symbol, section_id)
 
-                    if (scalar %KnownSymbols) {
-                        if ((! defined($KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"})) || 
$KnownSymbols{"$TMPL_DIR/$real_symbol:Long_Description"} != 1) {
-                            &LogWarning ($file, $., "Section $real_symbol is not defined in the 
$MODULE-sections.txt file.");
-                        }
-                    }
+                else:
+                    logging.info("SYMBOL DOCS found in source for : '%s' %d ", symbol, len(description))
+                    SourceSymbolDocs[symbol] = description
+                    SourceSymbolParams[symbol] = [params]
+                    # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO"
+                    #if (defined $DeclarationTypes{$symbol})
+                    #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol
+                    #
+                    SourceSymbolSourceFile[symbol] = ifile
+                    SourceSymbolSourceLine[symbol] = line_number
 
-                    @TRACE@("SECTION DOCS found in source for : '$real_symbol'\n");
-                    for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
-                        @TRACE@("   '".$params[$k]."'\n");
-                        $params[$k] = "\L$params[$k]";
-                        undef $key;
-                        if ($params[$k] eq "short_description") {
-                            $key = "$TMPL_DIR/$real_symbol:Short_Description";
-                        } elsif ($params[$k] eq "see_also") {
-                            $key = "$TMPL_DIR/$real_symbol:See_Also";
-                        } elsif ($params[$k] eq "title") {
-                            $key = "$TMPL_DIR/$real_symbol:Title";
-                        } elsif ($params[$k] eq "stability") {
-                            $key = "$TMPL_DIR/$real_symbol:Stability_Level";
-                        } elsif ($params[$k] eq "section_id") {
-                            $key = "$TMPL_DIR/$real_symbol:Section_Id";
-                        } elsif ($params[$k] eq "include") {
-                            $key = "$TMPL_DIR/$real_symbol:Include";
-                        } elsif ($params[$k] eq "image") {
-                            $key = "$TMPL_DIR/$real_symbol:Image";
-                        }
-                        if (defined($key)) {
-                            $SourceSymbolDocs{$key}=$params[$k+1];
-                            $SourceSymbolSourceFile{$key} = $file;
-                            $SourceSymbolSourceLine{$key} = $.;
-                        }
-                    }
-                    $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
-                    $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
-                    $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
-                    #$SourceSymbolTypes{$symbol} = "SECTION";
-                } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
-                    my $real_symbol = $1;
-                    my $key;
-                    my $section_id;
-
-                    @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
-                    for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
-                        @TRACE@("   '".$params[$k]."'\n");
-                        $params[$k] = "\L$params[$k]";
-                        undef $key;
-
-                        if ($params[$k] eq "short_description") {
-                            $key = "$TMPL_DIR/$real_symbol:Short_Description";
-                        } elsif ($params[$k] eq "see_also") {
-                            $key = "$TMPL_DIR/$real_symbol:See_Also";
-                        } elsif ($params[$k] eq "section_id") {
-                            $key = "$TMPL_DIR/$real_symbol:Section_Id";
-                        } elsif ($params[$k] eq "synopsis") {
-                            $key = "$TMPL_DIR/$real_symbol:Synopsis";
-                        } elsif ($params[$k] eq "returns") {
-                            $key = "$TMPL_DIR/$real_symbol:Returns";
-                        } elsif ($params[$k] =~ m/^(-.*)/) {
-                            $key = "$TMPL_DIR/$real_symbol:Options";
-                            my $opts;
-                            if (defined($SourceSymbolDocs{$key})) {
-                                $opts = $SourceSymbolDocs{$key};
-                            } else {
-                                $opts = [];
-                            }
-                            push (@{ $opts }, $1);
-                            push (@{ $opts }, $params[$k+1]);
-
-                            $SourceSymbolDocs{$key} = $opts;
-                            next;
-                        }
-                        if (defined($key)) {
-                            $SourceSymbolDocs{$key}=$params[$k+1];
-                            $SourceSymbolSourceFile{$key} = $file;
-                            $SourceSymbolSourceLine{$key} = $.;
-                        }
-                    }
-                    $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
-                    $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
-                    $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
 
-                    $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
-                    if (defined ($section_id) && $section_id !~ m/^\s*$/) {
-                        # Remove trailing blanks and use as is
-                        $section_id =~ s/\s+$//;
-                    } else {
-                        $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
-                    }
+                if since_desc:
+                    arr = since_desc.split('\n')
+                    since_desc = arr[0].strip()
+                    extra_lines = arr[1:]
+                    logging.info("Since(%s) : [%s]\n", symbol, since_desc)
+                    Since[symbol] = ConvertSGMLChars(symbol, since_desc)
+                    if len(extra_lines) > 0:
+                        common.LogWarning(ifile, line_number, "multi-line since docs found")
 
-                    &OutputProgramDBFile ($real_symbol, $section_id);
-
-                } else {
-                    @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
-                    $SourceSymbolDocs{$symbol} = $description;
-                    $SourceSymbolParams{$symbol} = [ @params ];
-                    # FIXME $SourceSymbolTypes{$symbol} = "STRUCT,SIGNAL,ARG,FUNCTION,MACRO";
-                    #if (defined $DeclarationTypes{$symbol}) {
-                    #    $SourceSymbolTypes{$symbol} = $DeclarationTypes{$symbol}
-                    #}
-                    $SourceSymbolSourceFile{$symbol} = $file;
-                    $SourceSymbolSourceLine{$symbol} = $.;
-                }
-
-                if ($since_desc) {
-                     ($since_desc, my @extra_lines) = split ("\n", $since_desc);
-                     $since_desc =~ s/^\s+//;
-                     $since_desc =~ s/\s+$//;
-                     @TRACE@("Since($symbol) : [$since_desc]\n");
-                     $Since{$symbol} = &ConvertSGMLChars ($symbol, $since_desc);
-                     if(scalar @extra_lines) {
-                         &LogWarning ($file, $., "multi-line since docs found");
-                     }
-                }
-
-                if ($stability_desc) {
-                    $stability_desc = &ParseStabilityLevel($stability_desc, $file, $., "Stability level for 
$symbol");
-                    $StabilityLevel{$symbol} = &ConvertSGMLChars ($symbol, $stability_desc);
-                }
-
-                if ($deprecated_desc) {
-                    if (!exists $Deprecated{$symbol}) {
-                         # don't warn for signals and properties
-                         #if ($symbol !~ m/::?(.*)/) {
-                         if (defined $DeclarationTypes{$symbol}) {
-                             &LogWarning ($file, $.,
-                                 "$symbol is deprecated in the inline comments, but no deprecation guards 
were found around the declaration.".
-                                 " (See the --deprecated-guards option for gtkdoc-scan.)");
-                         }
-                    }
-                    $Deprecated{$symbol} = &ConvertSGMLChars ($symbol, $deprecated_desc);
-                }
-            }
+                if stability_desc:
+                    stability_desc = ParseStabilityLevel(stability_desc, ifile, line_number, "Stability 
level for %s" % symbol)
+                    StabilityLevel[symbol] = ConvertSGMLChars(symbol, stability_desc)
+
+
+                if deprecated_desc:
+                    if symbol in Deprecated:
+                        # don't warn for signals and properties
+                        #if ($symbol !~ m/::?(.*)/)
+                        if symbol in DeclarationTypes:
+                            common.LogWarning(ifile, line_number,
+                                              "%s is deprecated in the inline comments, but no deprecation 
guards were found around the declaration. (See the --deprecated-guards option for gtkdoc-scan.)" % symbol)
 
-            $in_comment_block = 0;
-            next;
-        }
+
+                    Deprecated[symbol] = ConvertSGMLChars(symbol, deprecated_desc)
+
+
+
+            in_comment_block = False
+            continue
 
         # Get rid of ' * ' at start of every line in the comment block.
-        s%^\s*\*\s?%%;
+        line = re.sub(r'^\s*\*\s?', '', line)
         # But make sure we don't get rid of the newline at the end.
-        if (!$_) {
-            $_ = "\n";
-        }
-        @TRACE@("scanning :$_");
+        if not line.endswith('\n'):
+            line = line + "\n"
 
-        # If we haven't found the symbol name yet, look for it.
-        if (!$symbol) {
-            if (m%^\s*(SECTION:\s*\S+)%) {
-                $symbol = $1;
-                @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
-            } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
-                $symbol = $1;
-                @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
-            } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$%) {
-                $symbol = $1;
-                my $annotation = $2;
-                @TRACE@("SYMBOL DOCS found in source for : '$symbol'\n");
-                if (defined($annotation)) {
-                    chomp($annotation);
-                    if ($annotation ne "") {
-                        $SymbolAnnotations{$symbol} = $annotation;
-                        @TRACE@("remaining text for $symbol: '$annotation'\n");
-                    }
-                }
-            }
-            next;
-        }
+        logging.info("scanning : %s", line)
 
-        if ($in_part eq "description") {
+        # If we haven't found the symbol name yet, look for it.
+        if symbol:
+            m1 = re.search(r'^\s*(SECTION:\s*\S+)', line)
+            m2 = re.search(r'^\s*(PROGRAM:\s*\S+)', line)
+            m3 = re.search(r'^\s*([\w:-]*\w)\s*:?\s*(\(.+?\)\s*)*$', line)
+            if m1:
+                symbol = m1.group(1)
+                logging.info("SECTION DOCS found in source for : '%s'", symbol)
+            elif m2:
+                symbol = m2.group(1)
+                logging.info("PROGRAM DOCS found in source for : '%s'", symbol)
+            elif m3:
+                symbol = m3.group(1)
+                annotation = m3.group(2)
+                logging.info("SYMBOL DOCS found in source for : '%s'\n", symbol)
+                if annotation:
+                    annotation = annotation.strip()
+                    if annotation != '':
+                        SymbolAnnotations[symbol] = annotation
+                        logging.info("remaining text for $symbol: '%s'\n", annotation)
+
+            continue
+
+
+        if in_part == "description":
             # Get rid of 'Description:'
-            s%^\s*Description:%%;
-        }
+            line = re.sub(r'^\s*Description:', '', line)
+
 
-        if (m%^\s*(returns|return\s+value):%i) {
+        m = re.search(r'^\s*(returns|return\s+value):', line, flags=re.I)
+        m2 = re.search(r'^\s*since:', line, flags=re.I)
+        m3 = re.search(r'^\s*deprecated:', line, flags=re.I)
+        m4 = re.search(r'^\s*stability:', line, flags=re.I)
+
+        if m:
             # we're in param section and have not seen the blank line
-            if($in_part ne "") {
-              $return_desc = $';
-              $in_part = "return";
-              next;
-            }
-        } elsif (m%^\s*since:%i) {
+            if in_part != '':
+                return_desc = line[m.end():]
+                in_part = "return"
+                continue
+
+        if m2:
             # we're in param section and have not seen the blank line
-            if($in_part ne "param") {
-              $since_desc = $';
-              $in_part = "since";
-              next;
-            }
-        } elsif (m%^\s*deprecated:%i) {
+            if in_part != "param":
+                since_desc = line[m2.end():]
+                in_part = "since"
+                continue
+
+        elif m3:
             # we're in param section and have not seen the blank line
-            if($in_part ne "param") {
-              $deprecated_desc = $';
-              $in_part = "deprecated";
-              next;
-            }
-        } elsif (m%^\s*stability:%i) {
-            $stability_desc = $';
-            $in_part = "stability";
-            next;
-        }
-
-        if ($in_part eq "description") {
-            $description .= $_;
-            next;
-        } elsif ($in_part eq "return") {
-            $return_desc .= $_;
-            next;
-        } elsif ($in_part eq "since") {
-            $since_desc .= $_;
-            next;
-        } elsif ($in_part eq "stability") {
-            $stability_desc .= $_;
-            next;
-        } elsif ($in_part eq "deprecated") {
-            $deprecated_desc .= $_;
-            next;
-        }
+            if in_part != "param":
+                deprecated_desc = line[m.end():]
+                in_part = "deprecated"
+                continue
+
+        elif m4:
+            stability_desc = line[m.end():]
+            in_part = "stability"
+            continue
+
+
+        if in_part == "description":
+            description += line
+            continue
+        elif in_part == "return":
+            return_desc += line
+            continue
+        elif in_part == "since":
+            since_desc += line
+            continue
+        elif in_part == "stability":
+            stability_desc += line
+            continue
+        elif in_part == "deprecated":
+            deprecated_desc += line
+            continue
+
 
         # We must be in the parameters. Check for the empty line below them.
-        if (m%^\s*$%) {
-            $in_part = "description";
-            next;
-        }
+        if re.search(r'^\s*$', line):
+            in_part = "description"
+            continue
+
 
         # Look for a parameter name.
-        if (m%^\s*@(.+?)\s*:\s*%) {
-            my $param_name = $1;
-            my $param_desc = $';
+        m = re.search(r'^\s*@(.+?)\s*:\s*', line)
+        if m:
+            param_name = m.group(1)
+            param_desc = line[m.end():]
 
-            @TRACE@("Found parameter: $param_name\n");
+            logging.info("Found parameter: %s", param_name)
             # Allow varargs variations
-            if ($param_name =~ m/^\.\.\.$/) {
-                $param_name = "...";
-            }
-            @TRACE@("Found param for symbol $symbol : '$param_name'= '$_'");
-
-            push (@params, $param_name);
-            push (@params, $param_desc);
-            $current_param += $PARAM_FIELD_COUNT;
-            $in_part = "param";
-            next;
-        } elsif ($in_part eq "") {
-            @TRACE@("continuation for $symbol annotation '$_'");
-            my $annotation = $_;
-            $annotation =~ s/^\s+|\s+$//g ;
-            $SymbolAnnotations{$symbol} .= $annotation;
-            next;
-        }
+            if re.search(r'^\.\.\.$', param_name):
+                param_name = "..."
+
+            logging.info("Found param for symbol $symbol : '%s'= '%s'", param_name, line)
+
+            params.append(param_name)
+            params.append(param_desc)
+            current_param += PARAM_FIELD_COUNT
+            in_part = "param"
+            continue
+        elif in_part == '':
+            logging.info("continuation for %s annotation '%s'", symbol, line)
+            annotation = line
+            annotation = re.sub(r'^\s+|\s+$', '', annotation)
+            SymbolAnnotations[symbol] += annotation
+            continue
+
 
         # We must be in the middle of a parameter description, so add it on
         # to the last element in @params.
-        if ($current_param == -1) {
-            &LogWarning ($file, $., "Parsing comment block file : parameter expected, but got '$_'");
-        } else {
-            $params[$#params] .= $_;
-        }
-    }
-    close (SRCFILE);
-}
+        if current_param == -1:
+            common.LogWarning(file, line_number, "Parsing comment block file : parameter expected, but got 
'%s'" % line)
+        else:
+            params[len(params)] += line
+
+
+    SRCFILE.close()
+
 
 #############################################################################
 # Function    : OutputMissingDocumentation
@@ -4444,99 +4093,96 @@ sub ScanSourceFile {
 # Arguments   : none
 #############################################################################
 
-sub OutputMissingDocumentation {
-    my $old_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.txt";
-    my $new_undocumented_file = "$ROOT_DIR/$MODULE-undocumented.new";
+def OutputMissingDocumentation():
+    old_undocumented_file = os.path.join(ROOT_DIR, MODULE + "undocumented.txt")
+    new_undocumented_file = os.path.join(ROOT_DIR, MODULE + "-undocumented.new")
 
-    my $n_documented = 0;
-    my $n_incomplete = 0;
-    my $total = 0;
-    my $symbol;
-    my $percent;
-    my $msg;
-    my $buffer = "";
-    my $buffer_deprecated = "";
-    my $buffer_descriptions = "";
+    n_documented = 0
+    n_incomplete = 0
+    total = 0
+    symbol = None
+    percent = None
+    buffer = ''
+    buffer_deprecated = ''
+    buffer_descriptions = ''
 
-    open(UNDOCUMENTED, ">$new_undocumented_file")
-      || die "Can't create $new_undocumented_file";
+    UNDOCUMENTED = open(new_undocumented_file, 'w')
 
-    foreach $symbol (sort (keys (%AllSymbols))) {
-        # FIXME: should we print LogWarnings for undocumented stuff?
+    for symbol in sorted(AllSymbols.keys()):
+        # FIXME: should we print common.LogWarnings for undocumented stuff?
         # DEBUG
-        #my $ssfile = &GetSymbolSourceFile($symbol);
-        #my $ssline = &GetSymbolSourceLine($symbol);
-        #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . 
(defined($ssline)?$ssline:"0") . "\n";
+        #my $ssfile = &GetSymbolSourceFile($symbol)
+        #my $ssline = &GetSymbolSourceLine($symbol)
+        #my $location = "defined at " . (defined($ssfile)?$ssfile:"?") . ":" . 
(defined($ssline)?$ssline:"0") . "\n"
         # DEBUG
-        if ($symbol !~ 
/:(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)/) {
-            $total++;
-            if (exists ($AllDocumentedSymbols{$symbol})) {
-                $n_documented++;
-                if (exists ($AllIncompleteSymbols{$symbol})) {
-                    $n_incomplete++;
-                    $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
-                    #$buffer .= "\t0: ".$location;
-                }
-            } elsif (exists $Deprecated{$symbol}) {
-                if (exists ($AllIncompleteSymbols{$symbol})) {
-                    $n_incomplete++;
-                    $buffer_deprecated .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
-                    #$buffer .= "\t1a: ".$location;
-                } else {
-                    $buffer_deprecated .= $symbol . "\n";
-                    #$buffer .= "\t1b: ".$location;
-                }
-            } else {
-                if (exists ($AllIncompleteSymbols{$symbol})) {
-                    $n_incomplete++;
-                    $buffer .= $symbol . " (" . $AllIncompleteSymbols{$symbol} . ")\n";
-                    #$buffer .= "\t2a: ".$location;
-                } else {
-                    $buffer .= $symbol . "\n";
-                    #$buffer .= "\t2b: ".$location;
-                }
-            }
-        } elsif ($symbol =~ /:(Long_Description|Short_Description)/) {
-            $total++;
-            if (((exists ($SymbolDocs{$symbol})) && (length ($SymbolDocs{$symbol}) > 0))
-            || ((exists ($AllDocumentedSymbols{$symbol})) && (length ($AllDocumentedSymbols{$symbol}) > 0))) 
{
-              $n_documented++;
-            } else {
-              # cut off the leading namespace ($TMPL_DIR)
-              $symbol =~ m/^.*\/(.*)$/;
-              $buffer_descriptions .= $1 . "\n";
-            }
-        }
-    }
-
-    if ($total == 0) {
-      $percent = 100;
-    } else {
-      $percent = ($n_documented / $total) * 100.0;
-    }
-
-    printf UNDOCUMENTED "%.0f%% symbol docs coverage.\n", $percent;
-    print UNDOCUMENTED "$n_documented symbols documented.\n";
-    print UNDOCUMENTED "$n_incomplete symbols incomplete.\n";
-    print UNDOCUMENTED ($total - $n_documented) . " not documented.\n";
-
-    if ($buffer_deprecated ne "") {
-      $buffer .= "\n" . $buffer_deprecated;
-    }
-    if ($buffer_descriptions ne "") {
-      $buffer .= "\n" . $buffer_descriptions;
-    }
-    if ($buffer ne "") {
-      print UNDOCUMENTED "\n\n$buffer";
-    }
-    close (UNDOCUMENTED);
-
-    return &UpdateFileIfChanged ($old_undocumented_file, $new_undocumented_file, 0);
-
-    printf "%.0f%% symbol docs coverage", $percent;
-    print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - $n_documented) 
. " not documented)\n";
-    print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't 
include intro sections.\n";
-}
+        m = 
re.search(r':(Title|Long_Description|Short_Description|See_Also|Stability_Level|Include|Section_Id|Image)', 
symbol)
+        m2 = re.search(r':(Long_Description|Short_Description)', symbol)
+        if not m:
+            total += 1
+            if symbol in AllDocumentedSymbols:
+                n_documented += 1
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t0: ".$location
+
+            elif symbol in Deprecated:
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer_deprecated += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t1a: ".$location
+                else:
+                    buffer_deprecated += symbol + "\n"
+                    #$buffer += "\t1b: ".$location
+
+            else:
+                if symbol in AllIncompleteSymbols:
+                    n_incomplete += 1
+                    buffer += symbol + " (" + AllIncompleteSymbols[symbol] + ")\n"
+                    #$buffer += "\t2a: ".$location
+                else:
+                    buffer += symbol + "\n"
+                    #$buffer += "\t2b: ".$location
+
+
+        elif m2:
+            total += 1
+            if symbol in SymbolDocs and len(SymbolDocs[symbol]) > 0\
+               or symbol in AllDocumentedSymbols and len(AllDocumentedSymbols[symbol]) > 0:
+                n_documented += 1
+            else:
+                # cut off the leading namespace ($TMPL_DIR)
+                m = re.search(r'^.*\/(.*)$', symbol)
+                buffer_descriptions += m.group(1) + "\n"
+
+    if total == 0:
+        percent = 100
+    else:
+        percent = (n_documented / total) * 100.0
+
+
+    UNDOCUMENTED.write("%.0f%% symbol docs coverage.\n"%percent)
+    UNDOCUMENTED.write("%s symbols documented.\n" % n_documented)
+    UNDOCUMENTED.write("%s symbols incomplete.\n" % n_incomplete)
+    UNDOCUMENTED.write("%d not documented.\n" % (total - n_documented))
+
+    if buffer_deprecated != '':
+        buffer += "\n" + buffer_deprecated
+
+    if buffer_descriptions != '':
+        buffer += "\n" + buffer_descriptions
+
+    if buffer != '':
+        UNDOCUMENTED.write("\n\n" + buffer)
+
+    UNDOCUMENTED.close()
+
+    return common.UpdateFileIfChanged(old_undocumented_file, new_undocumented_file, 0)
+
+#    printf "%.0f%% symbol docs coverage", $percent
+#    print "($n_documented symbols documented, $n_incomplete symbols incomplete, " . ($total - 
$n_documented) . " not documented)\n"
+#    print "See $MODULE-undocumented.txt for a list of missing docs.\nThe doc coverage percentage doesn't 
include intro sections.\n"
+
 
 
 #############################################################################
@@ -4547,22 +4193,21 @@ sub OutputMissingDocumentation {
 # Arguments   : none
 #############################################################################
 
-sub OutputUndeclaredSymbols {
-    my $old_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.txt";
-    my $new_undeclared_file = "$ROOT_DIR/$MODULE-undeclared.new";
+def OutputUndeclaredSymbols():
+    old_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.txt")
+    new_undeclared_file = os.path.join(ROOT_DIR, MODULE + "-undeclared.new")
 
-    open(UNDECLARED, ">$new_undeclared_file")
-        || die "Can't create $new_undeclared_file";
+    UNDECLARED = open(new_undeclared_file, 'w')
 
-    if (%UndeclaredSymbols) {
-        print UNDECLARED (join("\n", sort keys %UndeclaredSymbols));
-        print UNDECLARED "\n";
-        print "See $MODULE-undeclared.txt for the list of undeclared symbols.\n"
-    }
-    close(UNDECLARED);
+    if UndeclaredSymbols:
+        UNDECLARED.write("\n".join(sorted(UndeclaredSymbols.keys())))
+        UNDECLARED.write("\n")
+        print("See %s-undeclared.txt for the list of undeclared symbols." % MODULE)
+
+    UNDECLARED.close()
+
+    return common.UpdateFileIfChanged(old_undeclared_file, new_undeclared_file, 0)
 
-    return &UpdateFileIfChanged ($old_undeclared_file, $new_undeclared_file, 0);
-}
 
 #############################################################################
 # Function    : OutputUnusedSymbols
@@ -4572,32 +4217,30 @@ sub OutputUndeclaredSymbols {
 # Arguments   : none
 #############################################################################
 
-sub OutputUnusedSymbols {
-    my $num_unused = 0;
-    my $old_unused_file = "$ROOT_DIR/$MODULE-unused.txt";
-    my $new_unused_file = "$ROOT_DIR/$MODULE-unused.new";
-
-    open (UNUSED, ">$new_unused_file")
-        || die "Can't open $new_unused_file";
-    my ($symbol);
-    foreach $symbol (sort keys (%Declarations)) {
-        if (!defined ($DeclarationOutput{$symbol})) {
-            print (UNUSED "$symbol\n");
-            $num_unused++;
-        }
-    }
-    foreach $symbol (sort (keys (%AllUnusedSymbols))) {
-        print (UNUSED "$symbol(" . $AllUnusedSymbols{$symbol} . ")\n");
-        $num_unused++;
-    }
-    close (UNUSED);
-    if ($num_unused != 0) {
-        &LogWarning ($old_unused_file, 1, "$num_unused unused declarations.".
-            "They should be added to $MODULE-sections.txt in the appropriate place.");
-    }
-
-    return &UpdateFileIfChanged ($old_unused_file, $new_unused_file, 0);
-}
+def OutputUnusedSymbols():
+    num_unused = 0
+    old_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.txt")
+    new_unused_file = os.path.join(ROOT_DIR, MODULE + "-unused.new")
+
+    UNUSED = open(new_unused_file, 'w')
+
+    for symbol in sorted(Declarations.keys()):
+        if not symbol in DeclarationOutput:
+            UNUSED.write("%s\n" % symbol)
+            num_unused += 1
+
+
+    for symbol in sorted(AllUnusedSymbols.keys()):
+        UNUSED.write(symbol + "(" + AllUnusedSymbols[symbol] + ")\n")
+        num_unused += 1
+
+    UNUSED.close()
+    if num_unused != 0:
+        common.LogWarning(old_unused_file, 1, "%d unused declarations. They should be added to 
%s-sections.txt in the appropriate place." % (num_unused, MODULE))
+
+
+    return common.UpdateFileIfChanged(old_unused_file, new_unused_file, 0)
+
 
 
 #############################################################################
@@ -4607,22 +4250,13 @@ sub OutputUnusedSymbols {
 # Arguments   : none
 #############################################################################
 
-sub OutputAllSymbols {
-     my $n_documented = 0;
-     my $total = 0;
-     my $symbol;
-     my $percent;
-     my $msg;
-
-     open (SYMBOLS, ">$ROOT_DIR/$MODULE-symbols.txt")
-          || die "Can't create $ROOT_DIR/$MODULE-symbols.txt: $!";
+def OutputAllSymbols():
+    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-symbols.txt"), 'w')
 
-     foreach $symbol (sort (keys (%AllSymbols))) {
-          print SYMBOLS $symbol . "\n";
-     }
+    for symbol in sorted(AllSymbols.keys()):
+        SYMBOLS.write(symbol + "\n")
+    SYMBOLS.close()
 
-     close (SYMBOLS);
-}
 
 #############################################################################
 # Function    : OutputSymbolsWithoutSince
@@ -4631,25 +4265,13 @@ sub OutputAllSymbols {
 # Arguments   : none
 #############################################################################
 
-sub OutputSymbolsWithoutSince {
-     my $n_documented = 0;
-     my $total = 0;
-     my $symbol;
-     my $percent;
-     my $msg;
-
-     open (SYMBOLS, ">$ROOT_DIR/$MODULE-nosince.txt")
-          || die "Can't create $ROOT_DIR/$MODULE-nosince.txt: $!";
-
-     foreach $symbol (sort (keys (%SourceSymbolDocs))) {
-         if (!defined $Since{$symbol}) {
-             print SYMBOLS $symbol . "\n";
-         }
-     }
-
-     close (SYMBOLS);
-}
+def OutputSymbolsWithoutSince():
+    SYMBOLS = open(os.path.join(ROOT_DIR, MODULE + "-nosince.txt"), 'w')
 
+    for symbol in sorted(SourceSymbolDocs.keys()):
+        if symbol in Since:
+            SYMBOLS.write(symbol + "\n")
+    SYMBOLS.close()
 
 #############################################################################
 # Function    : MergeSourceDocumentation
@@ -4663,258 +4285,229 @@ sub OutputSymbolsWithoutSince {
 # Arguments   : none
 #############################################################################
 
-sub MergeSourceDocumentation {
-    my $symbol;
-    my @Symbols;
+def MergeSourceDocumentation():
+    if len(SymbolDocs) > 0:
+        Symbols = SymbolDocs.keys()
+        logging.info("num existing entries: %d\n", len(Symbols))
 
-    if (scalar %SymbolDocs) {
-        @Symbols=keys (%SymbolDocs);
-        @TRACE@("num existing entries: ".(scalar @Symbols)."\n");
-    }
-    else {
+    else:
         # filter scanned declarations, with what we suppress from -sections.txt
-        my %tmp = ();
-        foreach $symbol (keys (%Declarations)) {
-            if (defined($KnownSymbols{$symbol}) && $KnownSymbols{$symbol} == 1) {
-                $tmp{$symbol}=1;
-            }
-        }
+        tmp = {}
+        for symbol in Declarations.keys():
+            if symbol in KnownSymbols and KnownSymbols[symbol] == 1:
+                tmp[symbol] = 1
+
+
         # , add the rest from -sections.txt
-        foreach $symbol (keys (%KnownSymbols)) {
-            if ($KnownSymbols{$symbol} == 1) {
-                $tmp{$symbol}=1;
-            }
-        }
+        for symbol in KnownSymbols.keys():
+            if KnownSymbols[symbol] == 1:
+                tmp[symbol] = 1
+
+
         # and add whats found in the source
-        foreach $symbol (keys (%SourceSymbolDocs)) {
-            $tmp{$symbol}=1;
-        }
-        @Symbols = keys (%tmp);
-        @TRACE@("num source entries: ".(scalar @Symbols)."\n");
-    }
-    foreach $symbol (@Symbols) {
-        $AllSymbols{$symbol} = 1;
+        for symbol in SourceSymbolDocs.keys():
+            tmp[symbol] = 1
 
-        my $have_tmpl_docs = 0;
+        Symbols = tmp.keys()
+        logging.info("num source entries: %d\n", len(Symbols))
+
+    for symbol in Symbols:
+        AllSymbols[symbol] = 1
+
+        have_tmpl_docs = 0
 
         ## see if the symbol is documented in template
-        my $tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
-        my $check_tmpl_doc =$tmpl_doc;
+        tmpl_doc = SymbolDocs.get(symbol, '')
+        check_tmpl_doc = tmpl_doc
         # remove all xml-tags and whitespaces
-        $check_tmpl_doc =~ s/<.*?>//g;
-        $check_tmpl_doc =~ s/\s//g;
+        check_tmpl_doc = re.sub(r'<.*?>', '', check_tmpl_doc)
+        check_tmpl_doc = re.sub(r'\s', '', check_tmpl_doc)
         # anything left ?
-        if ($check_tmpl_doc ne "") {
-            $have_tmpl_docs = 1;
-        } else {
+        if check_tmpl_doc != '':
+            have_tmpl_docs = 1
+        else:
             # if the docs have just an empty para, don't merge that.
-            $check_tmpl_doc = $tmpl_doc;
-            $check_tmpl_doc =~ s/(\s|\n)//msg;
-            if ($check_tmpl_doc eq "<para></para>") {
-               $tmpl_doc = "";
-            }
-        }
-
-        if (exists ($SourceSymbolDocs{$symbol})) {
-            my $type = $DeclarationTypes {$symbol};
-
-            @TRACE@("merging [$symbol] from source\n");
-
-            my $item = "Parameter";
-            if (defined ($type)) {
-                if ($type eq 'STRUCT') {
-                    $item = "Field";
-                } elsif ($type eq 'ENUM') {
-                    $item = "Value";
-                } elsif ($type eq 'UNION') {
-                    $item = "Field";
-                }
-            } else {
-                $type="SIGNAL";
-            }
-
-            my $src_doc = $SourceSymbolDocs{$symbol};
+            check_tmpl_doc = re.sub(r'(\s|\n)', '', tmpl_doc, flags=re.M|re.S)
+            if check_tmpl_doc == "<para></para>":
+                tmpl_doc = ''
+
+        if symbol in SourceSymbolDocs:
+            stype = DeclarationTypes[symbol]
+
+            logging.info("merging [%s] from source\n", symbol)
+
+            item = "Parameter"
+            if stype:
+                if stype == 'STRUCT':
+                    item = "Field"
+                elif stype == 'ENUM':
+                    item = "Value"
+                elif stype == 'UNION':
+                    item = "Field"
+            else:
+                stype = "SIGNAL"
+
+            src_doc = SourceSymbolDocs[symbol]
             # remove leading and training whitespaces
-            $src_doc =~ s/^\s+//;
-            $src_doc =~ s/\s+$//;
+            src_doc = src_doc.strip()
 
             # Don't output warnings for overridden titles as titles are
             # automatically generated in the -sections.txt file, and thus they
             # are often overridden.
-            if ($have_tmpl_docs && $symbol !~ m/:Title$/) {
+            m = re.search(r':Title$', symbol)
+            if have_tmpl_docs and not m:
                 # check if content is different
-                if ($tmpl_doc ne $src_doc) {
-                    #print "[$tmpl_doc] [$src_doc]\n";
-                    &LogWarning ($SourceSymbolSourceFile{$symbol}, $SourceSymbolSourceLine{$symbol},
-                        "Documentation in template 
".$SymbolSourceFile{$symbol}.":".$SymbolSourceLine{$symbol}." for $symbol being overridden by inline 
comments.");
-                }
-            }
+                if tmpl_doc != src_doc:
+                    #print "[$tmpl_doc] [$src_doc]\n"
+                    common.LogWarning(SourceSymbolSourceFile[symbol], SourceSymbolSourceLine[symbol],
+                                      "Documentation in template " + SymbolSourceFile[symbol] + ":" + 
SymbolSourceLine[symbol] + " for %s being overridden by inline comments." % symbol)
+
+            if src_doc != '':
+                AllDocumentedSymbols[symbol] = 1
 
-            if ($src_doc ne "") {
-                 $AllDocumentedSymbols{$symbol} = 1;
-            }
 
             # Do not add <para> to nothing, it breaks missing docs checks.
-            my $src_doc_para = "";
-            if ($src_doc ne "") {
-                $src_doc_para = $src_doc;
-            }
-
-            if ($symbol =~ m/$TMPL_DIR\/.+:Long_Description/) {
-                $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
-            } elsif ($symbol =~ m/$TMPL_DIR\/.+:.+/) {
+            src_doc_para = ''
+            if src_doc != '':
+                src_doc_para = src_doc
+
+
+            m = re.search(r'$TMPL_DIR\/.+:Long_Description', symbol)
+            m2 = re.search(TMPL_DIR + r'\/.+:.+', symbol)
+            if m:
+                SymbolDocs[symbol] = '%s%s' % (src_doc_para, tmpl_doc)
+            elif m2:
                 # For the title/summary/see also section docs we don't want to
                 # add any <para> tags.
-                $SymbolDocs{$symbol} = "$src_doc"
-            } else {
-                $SymbolDocs{$symbol} = "$src_doc_para$tmpl_doc";
-            }
+                SymbolDocs[symbol] = src_doc
+            else:
+                SymbolDocs[symbol] = '%s%s' % (src_doc_para, tmpl_doc)
 
             # merge parameters
-            if ($symbol =~ m/.*::.*/) {
+            if re.search(r'.*::.*', symbol):
                 # For signals we prefer the param names from the source docs,
                 # since the ones from the templates are likely to contain the
                 # artificial argn names which are generated by gtkdoc-scangobj.
-                $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
+                SymbolParams[symbol] = SourceSymbolParams[symbol]
                 # FIXME: we need to check for empty docs here as well!
-            } else {
+            else:
                 # The templates contain the definitive parameter names and order,
                 # so we will not change that. We only override the actual text.
-                my $tmpl_params = $SymbolParams{$symbol};
-                if (!defined ($tmpl_params)) {
-                    @TRACE@("No merge needed for $symbol\n");
-                    $SymbolParams{$symbol} = $SourceSymbolParams{$symbol};
+                tmpl_params = SymbolParams[symbol]
+                if tmpl_params:
+                    logging.info("No merge needed for %s\n", symbol)
+                    SymbolParams[symbol] = SourceSymbolParams[symbol]
                     #  FIXME: we still like to get the number of params and merge
                     #  1) we would noticed that params have been removed/renamed
                     #  2) we would catch undocumented params
                     #  params are not (yet) exported in -decl.txt so that we
                     #  could easily grab them :/
-                } else {
-                    my $params = $SourceSymbolParams{$symbol};
-                    my $j;
-                    @TRACE@("Merge needed for $symbol, tmpl_params: ",$#$tmpl_params,", source_params: 
",$#$params," \n");
-                    for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
-                        my $tmpl_param_name = $$tmpl_params[$j];
+                else:
+                    params = SourceSymbolParams[symbol]
+                    logging.info("Merge needed for %s, tmpl_params: %d, source_params: %d \n", symbol, 
len(tmpl_params), len(params))
+                    for j in range(0, len(tmpl_params), PARAM_FIELD_COUNT):
+                        tmpl_param_name = tmpl_params[j]
 
                         # Try to find the param in the source comment documentation.
-                        my $found = 0;
-                        my $k;
-                        @TRACE@("  try merge param $tmpl_param_name\n");
-                        for ($k = 0; $k <= $#$params; $k += $PARAM_FIELD_COUNT) {
-                            my $param_name = $$params[$k];
-                            my $param_desc = $$params[$k + 1];
-
-                            @TRACE@("    test param  $param_name\n");
+                        found = False
+                        logging.info("  try merge param %s\n", tmpl_param_name)
+                        for k in range(0, len(params), PARAM_FIELD_COUNT):
+                            param_name = params[k]
+                            param_desc = params[k + 1]
+
+                            logging.info("    test param  %s\n", param_name)
                             # We accept changes in case, since the Gnome source
                             # docs contain a lot of these.
-                            if ("\L$param_name" eq "\L$tmpl_param_name") {
-                                $found = 1;
+                            if param_name.lower() == tmpl_param_name.lower():
+                                found = True
 
                                 # Override the description.
-                                $$tmpl_params[$j + 1] = $param_desc;
+                                tmpl_params[j + 1] = param_desc
 
-                                # Set the name to "" to mark it as used.
-                                $$params[$k] = "";
-                                last;
-                            }
-                        }
+                                # Set the name to '' to mark it as used.
+                                params[k] = ''
+                                break
 
                         # If it looks like the parameters are there, but not
                         # in the right place, try to explain a bit better.
-                        if ((!$found) && ($src_doc =~ m/\@$tmpl_param_name:/)) {
-                            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                                "Parameters for $symbol must start on the line immediately after the 
function or macro name.");
-                        }
-                    }
+                        if found and re.search(r'\@%s:' % tmpl_param_name, src_doc):
+                            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                              "Parameters for %s must start on the line immediately after 
the function or macro name." % symbol)
 
                     # Now we output a warning if parameters have been described which
                     # do not exist.
-                    for ($j = 0; $j <= $#$params; $j += $PARAM_FIELD_COUNT) {
-                        my $param_name = $$params[$j];
-                        if ($param_name) {
+                    for j in range(0, len(params), PARAM_FIELD_COUNT):
+                        param_name = params[j]
+                        if param_name:
                             # the template builder cannot detect if a macro returns
                             # a result or not
-                            if(($type eq "MACRO") && ($param_name eq "Returns")) {
+                            if stype == "MACRO" and param_name == "Returns":
                                 # FIXME: do we need to add it then to tmpl_params[] ?
-                                my $num=$#$tmpl_params;
-                                @TRACE@("  adding Returns: to macro docs for $symbol.\n");
-                                $$tmpl_params[$num+1]="Returns";
-                                $$tmpl_params[$num+2]=$$params[$j+1];
-                                next;
-                            }
-                            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                                "$item described in source code comment block but does not exist. $type: 
$symbol $item: $param_name.");
-                        }
-                    }
-                }
-            }
-        } else {
-            if ($have_tmpl_docs) {
-                $AllDocumentedSymbols{$symbol} = 1;
-                @TRACE@("merging [$symbol] from template\n");
-            }
-            else {
-                @TRACE@("[$symbol] undocumented\n");
-            }
-        }
+                                num = len(tmpl_params)
+                                logging.info("  adding Returns: to macro docs for %s.", symbol)
+                                tmpl_params[num+1] = "Returns"
+                                tmpl_params[num+2] = params[j+1]
+                                continue
 
-        # if this symbol is documented, check if docs are complete
-        $check_tmpl_doc = defined ($SymbolDocs{$symbol}) ? $SymbolDocs{$symbol} : "";
-        # remove all xml-tags and whitespaces
-        $check_tmpl_doc =~ s/<.*?>//g;
-        $check_tmpl_doc =~ s/\s//g;
-        if ($check_tmpl_doc ne "") {
-            my $tmpl_params = $SymbolParams{$symbol};
-            if (defined ($tmpl_params)) {
-                my $type = $DeclarationTypes {$symbol};
-
-                my $item = "Parameter";
-                if (defined ($type)) {
-                    if ($type eq 'STRUCT') {
-                        $item = "Field";
-                    } elsif ($type eq 'ENUM') {
-                        $item = "Value";
-                    } elsif ($type eq 'UNION') {
-                        $item = "Field";
-                    }
-                } else {
-                    $type="SIGNAL";
-                }
+                            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                              "%s described in source code comment block but does not exist. 
%s: %s %s: %s." % (item, stype, symbol, item, param_name))
+
+        else:
+            if have_tmpl_docs:
+                AllDocumentedSymbols[symbol] = 1
+                logging.info("merging [%s] from template", symbol)
 
-                @TRACE@("Check param docs for $symbol, tmpl_params: ",$#$tmpl_params," entries, 
type=$type\n");
+            else:
+                logging.info("[%s] undocumented\n", symbol)
 
-                if ($#$tmpl_params > 0) {
-                    my $j;
-                    for ($j = 0; $j <= $#$tmpl_params; $j += $PARAM_FIELD_COUNT) {
+        # if this symbol is documented, check if docs are complete
+        check_tmpl_doc = SymbolDocs.get(symbol, '')
+        # remove all xml-tags and whitespaces
+        check_tmpl_doc = re.sub(r'<.*?>', '', check_tmpl_doc)
+        check_tmpl_doc = re.sub(r'\s', '', check_tmpl_doc)
+        if check_tmpl_doc != '':
+            tmpl_params = SymbolParams[symbol]
+            if tmpl_params:
+                stype = DeclarationTypes[symbol]
+
+                item = "Parameter"
+                if stype:
+                    if stype == 'STRUCT':
+                        item = "Field"
+                    elif stype == 'ENUM':
+                        item = "Value"
+                    elif stype == 'UNION':
+                        item = "Field"
+
+                else:
+                    stype = "SIGNAL"
+                logging.info("Check param docs for %s, tmpl_params: %s entries, type=%s\n", symbol, 
len(tmpl_params), stype)
+
+                if len(tmpl_params > 0):
+                    for j in range(0, len(tmpl_params), PARAM_FIELD_COUNT):
                         # Output a warning if the parameter is empty and
                         # remember for stats.
-                        my $tmpl_param_name = $$tmpl_params[$j];
-                        my $tmpl_param_desc = $$tmpl_params[$j + 1];
-                        if ($tmpl_param_name ne "void" && $tmpl_param_desc !~ m/\S/) {
-                            if (exists ($AllIncompleteSymbols{$symbol})) {
-                                $AllIncompleteSymbols{$symbol}.=", ".$tmpl_param_name;
-                            } else {
-                                $AllIncompleteSymbols{$symbol}=$tmpl_param_name;
-                            }
-                            &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                                "$item description for $symbol"."::"."$tmpl_param_name is missing in source 
code comment block.");
-                        }
-                    }
-                }
-                else {
-                    if ($#$tmpl_params == 0) {
-                        $AllIncompleteSymbols{$symbol}="<items>";
-                        &LogWarning (&GetSymbolSourceFile ($symbol), &GetSymbolSourceLine($symbol),
-                            "$item descriptions for $symbol are missing in source code comment block.");
-                    }
+                        tmpl_param_name = tmpl_params[j]
+                        tmpl_param_desc = tmpl_params[j + 1]
+                        if tmpl_param_name != "void" and not re.search(r'\S', tmpl_param_desc):
+                            if symbol in AllIncompleteSymbols[symbol]:
+                                AllIncompleteSymbols[symbol] += ", " + tmpl_param_name
+                            else:
+                                AllIncompleteSymbols[symbol] = tmpl_param_name
+
+                            common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                              "%s description for %s::%s is missing in source code comment 
block." % (item, symbol, tmpl_param_name))
+
+                else:
+                    if len(tmpl_params) == 0:
+                        AllIncompleteSymbols[symbol] = "<items>"
+                        common.LogWarning(GetSymbolSourceFile(symbol), GetSymbolSourceLine(symbol),
+                                          "%s descriptions for %s are missing in source code comment block." 
% (item, symbol))
+
                     # $#$tmpl_params==-1 means we don't know about parameters
                     # this unfortunately does not tell if there should be some
-                }
-            }
-        }
-   }
-   @TRACE@("num doc entries: ".(scalar %SymbolDocs)."\n");
-}
+    logging.info("num doc entries: %d\n", len(SymbolDocs))
+
 
 #############################################################################
 # Function    : IsEmptyDoc
@@ -4922,19 +4515,18 @@ sub MergeSourceDocumentation {
 #               it only consist of whitespace or e.g. FIXME.
 # Arguments   : the doc-string
 #############################################################################
-sub IsEmptyDoc {
-    my ($doc) = @_;
+def IsEmptyDoc(doc):
 
-    if ($doc =~ /^\s*$/) {
-        return 1;
-    }
+    if re.search(r'^\s*$', doc):
+        return True
 
-    if ($doc =~ /^\s*<para>\s*(FIXME)?\s*<\/para>\s*$/) {
-        return 1;
-    }
 
-    return 0;
-}
+    if re.search(r'^\s*<para>\s*(FIXME)?\s*<\/para>\s*$', doc):
+        return True
+
+
+    return False
+
 
 #############################################################################
 # Function    : ConvertMarkDown
@@ -4946,13 +4538,10 @@ sub IsEmptyDoc {
 # Arguments   : the symbol name, the doc-string
 #############################################################################
 
-sub ConvertMarkDown {
-    my ($symbol, $text) = @_;
+def ConvertMarkDown(symbol, text):
+    text = MarkDownParse(text, symbol)
+    return text
 
-    $text = &MarkDownParse ($text, $symbol);
-
-    return $text
-}
 
 # SUPPORTED MARKDOWN
 # ==================
@@ -4988,728 +4577,719 @@ sub ConvertMarkDown {
 
 # TODO(ensonic): it would be nice to add id parameters to the refsect2 elements
 
-sub MarkDownParseBlocks {
-  my ($linesref, $symbol, $context) = @_;
-  my $line;
-  my @md_blocks = ();
-  my $md_block = { type => "" };
-
- OUTER: foreach $line (@$linesref) {
-    my $first_char = substr ($line, 0, 1);
-    my $deindented_line;
-
-    @TRACE@("in '".$md_block->{"type"}."' state, parsing '$line'");
-
-    if ($md_block->{"type"} eq "markup") {
-      if (!$md_block->{"closed"}) {
-        if (index ($line, $md_block->{"start"}) != -1) {
-          $md_block->{"depth"}++;
-        }
-        if (index ($line, $md_block->{"end"}) != -1) {
-          if ($md_block->{"depth"} > 0) {
-            $md_block->{"depth"}--;
-          } else {
-            @TRACE@("closing tag '$line'");
-            $md_block->{"closed"} = 1;
+def MarkDownParseBlocks(linesref, symbol, context):
+  md_blocks = []
+  md_block = {"type": ''}
+
+  # FIXME the original code had OUTER tag here but there does not seem to be inner loops.
+  for line in linesref:
+    first_char = line[0]
+
+    logging.info("in '" + md_block["type"] + "' state, parsing '%s'" % line)
+
+    if md_block["type"] == "markup":
+      if not md_block["closed"]:
+        if md_block["start"] in line:
+          md_block["depth"] += 1
+
+        if md_block["end"] in line:
+          if md_block["depth"] > 0:
+            md_block["depth"] -= 1
+          else:
+            logging.info("closing tag '%s'", line)
+            md_block["closed"] = 1
             # TODO(ensonic): reparse inner text with MarkDownParseLines?
-          }
-        }
-        $md_block->{"text"} .= "\n" . $line;
-        @TRACE@("add to markup");
-        next OUTER;
-      }
-    }
 
-    $deindented_line = $line;
-    $deindented_line =~ s/^\s+//;
+        md_block["text"] += "\n" + line
+        logging.info("add to markup")
+        continue
+
+    deindented_line = line.lstrip()
 
-    if ($md_block->{"type"} eq "heading") {
+    m = re.search(r'^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
+    if md_block["type"] == "heading":
       # a heading is ended by any level less than or equal
-      if ($md_block->{"level"} == 1) {
-        if ($line =~ /^={4,}[ \t]*$/) {
-          my $text = pop @{$md_block->{"lines"}};
-          $md_block->{"interrupted"} = 0;
-          push @md_blocks, $md_block;
-
-          $md_block = { type => "heading",
-                        text => $text,
-                        lines => [],
-                        level => 1 };
-          next OUTER;
-        } elsif ($line =~ /^[#][ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
-          $md_block->{"interrupted"} = 0;
-          push @md_blocks, $md_block;
-
-          $md_block = { type => "heading",
-                        text => $1,
-                        id => $2,
-                        lines => [],
-                        level => 1 };
-          next OUTER;
-        } else {
+      if md_block["level"] == 1:
+        if re.search(r'^={4,}[ \t]*$', line):
+          text = md_block["lines"].pop()
+          md_block["interrupted"] = 0
+          md_blocks.append(md_block)
+
+          md_block = {'type': "heading",
+                      'text': text,
+                      'lines': [],
+                      'level': 1,
+                     }
+          continue
+        elif m:
+          md_block["interrupted"] = 0
+          md_blocks.append(md_block)
+
+          md_block = {'type': "heading",
+                      'text': m.group(1),
+                      'id': m.group(2),
+                      'lines': [],
+                      'level': 1,
+                     }
+          continue
+        else:
           # push lines into the block until the end is reached
-          push @{$md_block->{"lines"}}, $line;
-          next OUTER;
-        }
-      } else {
-        if ($line =~ /^[=]{4,}[ \t]*$/) {
-          my $text = pop @{$md_block->{"lines"}};
-          $md_block->{"interrupted"} = 0;
-          push @md_blocks, $md_block;
-
-          $md_block = { type => "heading",
-                        text => $text,
-                        lines => [],
-                        level => 1 };
-          next OUTER;
-        } elsif ($line =~ /^[-]{4,}[ \t]*$/) {
-          my $text = pop @{$md_block->{"lines"}};
-          $md_block->{"interrupted"} = 0;
-          push @md_blocks, $md_block;
-
-          $md_block = { type => "heading",
-                        text => $text,
-                        lines => [],
-                        level => 2 };
-          next OUTER;
-        } elsif ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
-          $md_block->{"interrupted"} = 0;
-          push @md_blocks, $md_block;
-
-          $md_block = { type => "heading",
-                        text => $2,
-                        id => $3,
-                        lines => [],
-                        level => length($1) };
-          next OUTER;
-        } else {
+          md_block["lines"].append(line)
+          continue
+
+      else:
+        m2 = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
+        if re.search(r'^[=]{4,}[ \t]*$', line):
+          text = md_block["lines"].pop()
+          md_block["interrupted"] = 0
+          md_blocks.append(md_block)
+
+          md_block = {'type': "heading",
+                      'text': text,
+                      'lines': [],
+                      'level': 1,
+          }
+          continue
+        elif re.search(r'^[-]{4,}[ \t]*$', line):
+          text = md_block["lines"].pop()
+          md_block["interrupted"] = 0
+          md_blocks.append(md_block)
+
+          md_block = {'type': "heading",
+                      'text': text,
+                      'lines': [],
+                      'level': 2,
+                      }
+          continue
+        elif m2:
+          md_block["interrupted"] = 0
+          md_blocks.append(md_block)
+
+          md_block = {'type': "heading",
+                      'text': m2.group(2),
+                      'id': m2.group(3),
+                      'lines': [],
+                      'level': len(m2.group(1))
+                      }
+          continue
+        else:
           # push lines into the block until the end is reached
-          push @{$md_block->{"lines"}}, $line;
-          next OUTER;
-        }
-      }
-    } elsif ($md_block->{"type"} eq "code") {
-      if ($line =~ /^[ \t]*\]\|(.*)/) {
-        push @md_blocks, $md_block;
-        $md_block = { type => "paragraph",
-                      text => "$1",
-                      lines => [] };
-      } else {
-        push @{$md_block->{"lines"}}, $line;
-      }
-      next OUTER;
-    }
-
-    if ($deindented_line eq "") {
-      $md_block->{"interrupted"} = 1;
-      next;
-    }
-
-    if ($md_block->{"type"} eq "quote") {
-      if (!$md_block->{"interrupted"}) {
-        $line =~ s/^[ ]*>[ ]?//;
-        push @{$md_block->{"lines"}}, $line;
-        next OUTER;
-      }
-    } elsif ($md_block->{"type"} eq "li") {
-      my $marker = $md_block->{"marker"};
-      if ($line =~ /^([ ]{0,3})($marker)[ ](.*)/) {
-        my $indentation = $1;
-        if ($md_block->{"indentation"} ne $indentation) {
-          push @{$md_block->{"lines"}}, $line;
-        } else {
-          my $lines = $3;
-          my $ordered = $md_block->{"ordered"};
-          $lines =~ s/^[ ]{0,4}//;
-          $md_block->{"last"} = 0;
-          push @md_blocks, $md_block;
-          $md_block = { type => "li",
-                        ordered => $ordered,
-                        indentation => $indentation,
-                        marker => $marker,
-                        first => 0,
-                        last => 1,
-                        lines => [ $lines ] };
-        }
-        next OUTER;
-      }
-
-      if ($md_block->{"interrupted"}) {
-        if ($first_char eq " ") {
-          push @{$md_block->{"lines"}}, "";
-          $line =~ s/^[ ]{0,4}//;
-          push @{$md_block->{"lines"}}, $line;
-          $md_block->{"interrupted"} = 0;
-          next OUTER;
-        }
-      } else {
-        $line =~ s/^[ ]{0,4}//;
-        push @{$md_block->{"lines"}}, $line;
-        next OUTER;
-      }
-    }
+          md_block["lines"].append(line)
+          continue
+    elif md_block["type"] == "code":
+      m3 = re.search(r'^[ \t]*\]\|(.*)', line)
+      if m3:
+        md_blocks.append(md_block)
+        md_block = {'type': "paragraph",
+                    'text': "$1",
+                    'lines': [],
+                    }
+      else:
+        md_block["lines"].append(line)
+
+      continue
+
+    if deindented_line == '':
+      md_block["interrupted"] = 1
+      continue
+
+
+    if md_block["type"] == "quote":
+      if not md_block["interrupted"]:
+        line = re.sub(r'^[ ]*>[ ]?', '', line)
+        md_block["lines"].append(line)
+        continue
+
+    elif md_block["type"] == "li":
+      marker = md_block["marker"]
+      m4 = re.search(r'^([ ]{0,3})($marker)[ ](.*)', line)
+      if m4:
+        indentation = m.group(1)
+        if md_block["indentation"] != indentation:
+          md_block["lines"].append(line)
+        else:
+          lines = m4.group(3)
+          ordered = md_block["ordered"]
+          lines = re.sub(r'^[ ]{0,4}', '', lines)
+          md_block["last"] = 0
+          md_blocks.append(md_block)
+          md_block = {'type': "li",
+                      'ordered': ordered,
+                      'indentation': indentation,
+                      'marker': marker,
+                      'first': 0,
+                      'last': 1,
+                      'lines': [lines],
+                      }
+
+        continue
+
+      if md_block["interrupted"]:
+        if first_char == " ":
+          md_block["lines"].append('')
+          line =  re.sub(r'^[ ]{0,4}', '', line)
+          md_block["lines"].append(line)
+          md_block["interrupted"] = 0
+          continue
+
+      else:
+        line = re.sub(r'^[ ]{0,4}', '', line)
+        md_block["lines"].append(line)
+        continue
 
     # indentation sensitive types
-    @TRACE@("parsing '$line'");
-
-    if ($line =~ /^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$/) {
+    logging.info("parsing '%s'", line)
+
+    m5 = re.search(r'^([#]{1,2})[ \t]+(.+?)[ \t]*[#]*[ \t]*(?:{#([^}]+)})?[ \t]*$', line)
+    m6 = re.search(r'^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?', line)
+    m7 = re.search(r'^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>', line)
+    m8 = re.search(r'^([ ]*)[*+-][ ](.*)', line)
+    m9 = re.search(r'^[ ]*>[ ]?(.*)', line)
+    if m5:
       # atx heading (#)
-      push @md_blocks, $md_block;
+      md_blocks.append(md_block)
 
-      $md_block = { type => "heading",
-                    text => $2,
-                    id => $3,
-                    lines => [],
-                    level => length($1) };
+      md_block = {'type': "heading",
+                  'text': m5.group(2),
+                  'id:': m5.group(3),
+                  'lines': [],
+                  'level': len(m5.group(1)),
+                  }
 
-      next OUTER;
-    } elsif ($line =~ /^={4,}[ \t]*$/) {
+      continue
+    elif re.search(r'^={4,}[ \t]*$', line):
       # setext heading (====)
 
-      if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
-        push @md_blocks, $md_block;
-        $md_block->{"type"} = "heading";
-        $md_block->{"lines"} = [];
-        $md_block->{"level"} = 1;
-      }
-
-      next OUTER;
-    } elsif ($line =~ /^-{4,}[ \t]*$/) {
+      if md_block["type"] == "paragraph" and md_block["interrupted"]:
+        md_blocks.append(md_block)
+        md_block["type"] = "heading"
+        md_block["lines"] = []
+        md_block["level"] = 1
+      continue
+    elif re.search(r'^-{4,}[ \t]*$/', line):
       # setext heading (-----)
 
-      if ($md_block->{"type"} eq "paragraph" && $md_block->{"interrupted"}) {
-        push @md_blocks, $md_block;
-        $md_block->{"type"} = "heading";
-        $md_block->{"lines"} = [];
-        $md_block->{"level"} = 2;
-      }
+      if md_block["type"] == "paragraph" and md_block["interrupted"]:
+        md_blocks.append(md_block)
+        md_block["type"] = "heading"
+        md_block["lines"] = []
+        md_block["level"] = 2
 
-      next OUTER;
-    } elsif ($line =~ /^[ \t]*\|\[[ ]*(?:<!-- language="([^"]+?)" -->)?/) {
+      continue
+    elif m6:
       # code
-      $md_block->{"interrupted"} = 1;
-      push @md_blocks, $md_block;
-      $md_block = { type => "code",
-                    language => $1,
-                    lines => [] };
-      next OUTER;
-    }
+      md_block["interrupted"] = 1
+      md_blocks.append(md_block)
+      md_block = {'type': "code",
+                  'language': m6.group(1),
+                  'lines': [],
+                  }
+      continue
 
     # indentation insensitive types
-    if ($line =~ /^[ ]*<!DOCTYPE/) {
-      push @md_blocks, $md_block;
-
-      $md_block = { type   => "markup",
-                    text   => $deindented_line,
-                    start  => "<",
-                    end    => ">",
-                    closed => 0,
-                    depth  => 0 };
-
-    } elsif ($line =~ /^[ ]*<\??(\w+)[^>]*([\/\?])?[ \t]*>/) {
+    if re.search(r'^[ ]*<!DOCTYPE/', line):
+      md_blocks.append(md_block)
+
+      md_block = {'type'   : "markup",
+                  'text'   : deindented_line,
+                  'start'  : "<",
+                  'end'    : ">",
+                  'closed' : 0,
+                  'depth'  : 0,
+                  }
+
+    elif m7:
       # markup, including <?xml version="1.0"?>
-      my $tag = $1;
-      my $is_self_closing = defined($2);
+      tag = m7.group(1)
+      is_self_closing = m7.group(2) is not None
 
       # skip link markdown
       # TODO(ensonic): consider adding more uri schemes (ftp, ...)
-      if ($tag =~ /^https?/) {
-        @TRACE@("skipping link '$tag'");
-      } else {
+      if re.search(r'https?', tag):
+        logging.info("skipping link '%s'", tag)
+      else:
         # for TEXT_LEVEL_ELEMENTS, we want to keep them as-is in the paragraph
         # instead of creation a markdown block.
-        my $scanning_for_end_of_text_level_tag = (
-            $md_block->{"type"} eq "paragraph" &&
-            defined($md_block->{"start"}) &&
-            !$md_block->{"closed"});
-        @TRACE@("markup found '$tag', scanning $scanning_for_end_of_text_level_tag ?");
-        if (!$MD_TEXT_LEVEL_ELEMENTS{$tag} && !$scanning_for_end_of_text_level_tag) {
-          push @md_blocks, $md_block;
-
-          if ($is_self_closing) {
-            @TRACE@("self-closing docbook '$tag'");
-            $md_block = { type => "self-closing tag",
-                          text => $deindented_line };
-            $is_self_closing = 0;
-            next OUTER;
-          }
-
-          @TRACE@("new markup '$tag'");
-          $md_block = { type   => "markup",
-                        text   => $deindented_line,
-                        start  => "<" . $tag . ">",
-                        end    => "</" . $tag . ">",
-                        closed => 0,
-                        depth  => 0 };
-          if ($deindented_line =~ /<\/$tag>/) {
-            $md_block->{"closed"} = 1;
-          }
-          next OUTER;
-        } else {
-          if ($MD_TEXT_LEVEL_ELEMENTS{$tag}) {
-            @TRACE@("text level docbook '$tag' in '".$md_block->{"type"}."' state");
+        scanning_for_end_of_text_level_tag = (
+            md_block["type"] == "paragraph" and
+            'start' in md_block and
+            not md_block["closed"])
+        logging.info("markup found '%s', scanning %s ?", tag, scanning_for_end_of_text_level_tag)
+        if not MD_TEXT_LEVEL_ELEMENTS[tag] and not scanning_for_end_of_text_level_tag:
+          md_blocks.append(md_block)
+
+          if is_self_closing:
+            logging.info("self-closing docbook '%s'", tag)
+            md_block = {'type': "self-closing tag",
+                        'text': deindented_line,
+                        }
+            is_self_closing = 0
+            continue
+
+          logging.info("new markup '%s'", tag)
+          md_block = {'type'   : "markup",
+                      'text'   : deindented_line,
+                      'start'  : "<" + tag + ">",
+                      'end'    : "</" + tag + ">",
+                      'closed' : 0,
+                      'depth'  : 0,
+                      }
+          if re.search(r'<\/%s>' % tag, deindented_line):
+            md_block["closed"] = 1
+
+          continue
+        else:
+          if MD_TEXT_LEVEL_ELEMENTS[tag]:
+            logging.info("text level docbook '%s' in '%s' state", tag, md_block["type"])
             # TODO(ensonic): handle nesting
-            if (!$scanning_for_end_of_text_level_tag) {
-              if ($deindented_line !~ /<\/$tag>/) {
-                @TRACE@("new text level markup '$tag'");
-                $md_block->{"start"} = "<" . $tag . ">";
-                $md_block->{"end"} = "</" . $tag . ">";
-                $md_block->{"closed"} = 0;
-                @TRACE@("scanning for end of '$tag'");
-              }
-            } else {
-              if ($deindented_line =~ /$md_block->{"end"}/) {
-                $md_block->{"closed"} = 1;
-                @TRACE@("found end of '$tag'");
-              }
-            }
-          }
-        }
-      }
-    } elsif ($line =~ /^([ ]*)[*+-][ ](.*)/) {
+            if scanning_for_end_of_text_level_tag:
+              if not re.search(r'<\/%s>' % tag, deindented_line):
+                logging.info("new text level markup '%s'", tag)
+                md_block["start"] = "<" + tag + ">"
+                md_block["end"] = "</" + tag + ">"
+                md_block["closed"] = 0
+                logging.info("scanning for end of '%s'", tag)
+
+            else:
+              if re.search(md_block["end"], deindented_line):
+                md_block["closed"] = 1
+                logging.info("found end of '%s'", tag)
+
+    elif m8:
       # li
-      push @md_blocks, $md_block;
-      my $lines = $2;
-      my $indentation = $1;
-      $lines =~ s/^[ ]{0,4}//;
-      $md_block = { type => "li",
-                    ordered => 0,
-                    indentation => $indentation,
-                    marker => "[*+-]",
-                    first => 1,
-                    last => 1,
-                    lines => [ $lines ] };
-      next OUTER;
-    } elsif ($line =~ /^[ ]*>[ ]?(.*)/) {
-      push @md_blocks, $md_block;
-      $md_block = { type => "quote",
-                    lines => [ $1 ] };
-      next OUTER;
-    }
+      md_blocks.append(md_block)
+      lines = m8.group(2)
+      indentation = m8.group(1)
+      lines = re.sub(r'^[ ]{0,4}', '', lines)
+      md_block = {'type': "li",
+                  'ordered': 0,
+                  'indentation': indentation,
+                  'marker': "[*+-]",
+                  'first': 1,
+                  'last': 1,
+                  'lines': [lines],
+                  }
+      continue
+    elif m9:
+      md_blocks.append(md_block)
+      md_block = {'type': "quote",
+                  'lines': [m9.group(1)],
+                  }
+      continue
+
 
     # list item
-    if ($line =~ /^([ ]{0,4})\d+[.][ ]+(.*)/) {
-      push @md_blocks, $md_block;
-      my $lines = $2;
-      my $indentation = $1;
-      $lines =~ s/^[ ]{0,4}//;
-
-      $md_block = { type => "li",
-                    ordered => 1,
-                    indentation => $indentation,
-                    marker => "\\d+[.]",
-                    first => 1,
-                    last => 1,
-                    lines => [ $lines ] };
-
-      next;
-    }
+    m10 = re.search(r'^([ ]{0,4})\d+[.][ ]+(.*)', line)
+    if m10:
+      md_blocks.append(md_block)
+      lines = m10.group(2)
+      indentation = m10.group(1)
+      lines = re.sub(r'^[ ]{0,4}', '', lines)
+
+      md_block = {'type': "li",
+                  'ordered': 1,
+                  'indentation': indentation,
+                  'marker': "\\d+[.]",
+                  'first': 1,
+                  'last': 1,
+                  'lines': [lines],
+                  }
+
+      continue
+
 
     # paragraph
-    if ($md_block->{"type"} eq "paragraph") {
-      if ($md_block->{"interrupted"}) {
-        push @md_blocks, $md_block;
-        $md_block = { type => "paragraph",
-                      interrupted => 0,
-                      text => $line };
-        @TRACE@("new paragraph due to interrupted");
-      } else {
-        $md_block->{"text"} .= "\n" . $line;
-        @TRACE@("add to paragraph");
-      }
-    } else {
-      push @md_blocks, $md_block;
-      $md_block = { type => "paragraph",
-                    text => $line };
-      @TRACE@("new paragraph due to different block type");
-    }
-  }
-
-  push @md_blocks, $md_block;
-
-  shift @md_blocks;
-
-  return @md_blocks;
-}
+    if md_block["type"] == "paragraph":
+      if md_block["interrupted"]:
+        md_blocks.append(md_block)
+        md_block = {'type': "paragraph",
+                    'interrupted': 0,
+                    'text': line,
+                    }
+        logging.info("new paragraph due to interrupted")
+      else:
+        md_block["text"] += "\n" + line
+        logging.info("add to paragraph")
+
+    else:
+      md_blocks.append(md_block)
+      md_block = {'type': "paragraph",
+                  'text': line,
+                  }
+      logging.info("new paragraph due to different block type")
 
-sub MarkDownParseSpanElementsInner {
-  my ($text, $markersref) = @_;
-  my $markup = "";
-  my %markers = map { $_ => 1 } @$markersref;
-
-  while ($text ne "") {
-    my $closest_marker = "";
-    my $closest_marker_index = 0;
-    my $closest_marker_position = -1;
-    my $text_marker = "";
-    my $i = 0;
-    my $offset = 0;
-    my @markers_rest;
-    my $marker;
-    my $use;
-
-    while ( ($marker, $use) = each %markers ) {
-      my $marker_position;
-
-      if (!$use) {
-        next;
-      }
-
-      $marker_position = index ($text, $marker);
-
-      if ($marker_position < 0) {
-        $markers{$marker} = 0;
-        next;
-      }
-
-      if ($closest_marker eq "" || $marker_position < $closest_marker_position) {
-        $closest_marker = $marker;
-        $closest_marker_index = $i;
-        $closest_marker_position = $marker_position;
-      }
-    }
-
-    if ($closest_marker_position >= 0) {
-      $text_marker = substr ($text, $closest_marker_position);
-    }
-
-    if ($text_marker eq "") {
-      $markup .= $text;
-      $text = "";
-      next; # last
-    }
-
-    $markup .= substr ($text, 0, $closest_marker_position);
-    $text = substr ($text, $closest_marker_position);
-    @markers_rest = map { $markers{$_} ? ($_ eq $closest_marker ? () : $_) : () } keys %markers;
-
-    if ($closest_marker eq "![" || $closest_marker eq "[") {
-      my %element;
-
-      if (index ($text, "]") && $text =~ /\[((?:[^][]|(?R))*)\]/) {
-        my $remaining_text;
-
-        %element = ( "!" => (substr ($text, 0, 1) eq "!"),
-                     "a" => $1 );
-
-        $offset = length ($&);
-        if ($element{"!"}) {
-          $offset++;
-        }
-
-        $remaining_text = substr ($text, $offset);
-        if ($remaining_text =~ /^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)/) {
-          $element{"»"} = $1;
-          if (defined ($2)) {
-            $element{"#"} = $2;
-          }
-          $offset += length ($&);
-        } elsif ($remaining_text =~ /^\s*\[([^\]<]*?)\]/) {
-          $element{"ref"} = $1;
-          $offset += length ($&);
-        } else {
-          undef %element;
-        }
-      }
-
-      if (%element) {
-        if ($element{"»"}) {
-          $element{"»"} =~ s/&/&amp;/g;
-          $element{"»"} =~ s/</&lt;/g;
-        }
-        if ($element{"!"}) {
-          $markup .= "<inlinemediaobject><imageobject><imagedata fileref=\"" . $element{"»"} . 
"\"></imagedata></imageobject>";
-
-          if (defined ($element{"a"})) {
-            $markup .= "<textobject><phrase>" . $element{"a"} . "</phrase></textobject>";
-          }
+  md_blocks.append(md_block)
+
+  return md_blocks[1:]
 
-          $markup .= "</inlinemediaobject>";
-        } elsif ($element{"ref"}) {
-          $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
-          $markup .= "<link linkend=\"" . $element{"ref"} . "\"";
 
-          if (defined ($element{"#"})) {
-            # title attribute not supported
-          }
+def MarkDownParseSpanElementsInner(text, markersref):
+  markup = ''
+  markers = {}
+  for i in markersref:
+      markers[i] = 1
+
+  while text != '':
+    closest_marker = ''
+    # closest_marker_index = 0
+    closest_marker_position = -1
+    text_marker = ''
+    # i = 0
+    offset = 0
+    markers_rest = []
+
+    for marker, use in markers.items():
+      if not use:
+        continue
+
+
+      marker_position = text.find(marker)
+
+      if marker_position < 0:
+        markers[marker] = 0
+        continue
+
+
+      if closest_marker == '' or marker_position < closest_marker_position:
+        closest_marker = marker
+        # closest_marker_index = i
+        closest_marker_position = marker_position
+
+    if closest_marker_position >= 0:
+      text_marker = text[closest_marker_position:]
+
+
+    if text_marker == '':
+      markup += text
+      text = ''
+      continue # last
+
+
+    markup += text[0:closest_marker_position]
+    text = text[closest_marker_position:]
+    markers_rest = []
+    for key in markers.keys():
+        if markers[key]:
+            if key == closest_marker:
+                markers_rest.append([])
+            else:
+                markers_rest.append(key)
+        else:
+            markers_rest.append([])
+
+    if closest_marker == "![" or closest_marker == "[":
+      element = {}
+
+      m = re.search(r'\[((?:[^][]|(?R))*)\]', text)
+      if ']' in text and m:
+
+        element = {"!": text[0] == "!",
+                   "a": m.group(1),
+                   }
+
+        offset = len(m.group(0))
+        if element["!"]:
+          offset+=1
+
+
+        remaining_text = text[offset:]
+        m2 = re.search(r'''^\([ ]*([^)'"]*?)(?:[ ]+['"](.+?)['"])?[ ]*\)''', remaining_text)
+        m3 = re.search(r'^\s*\[([^\]<]*?)\]', remaining_text)
+        if m2:
+          element["»"] = m2.group(1)
+          if m2.group(2):
+            element["#"] = m2.group(2)
+            offset += len(m2.group(0))
+        elif m3:
+          element["ref"] = m3.group(1)
+          offset += len(m3.group(0))
+        else:
+          element = None
+
+      if element:
+        if element["»"]:
+          element["»"] = element["»"].replace('&', '&amp;')
+          element["»"] = element["»"].replace('<', '&lt;')
 
-          $markup .= ">" . $element{"a"} . "</link>";
-        } else {
-          $element{"a"} = &MarkDownParseSpanElementsInner ($element{"a"}, \@markers_rest);
-          $markup .= "<ulink url=\"" . $element{"»"} . "\"";
+        if element["!"]:
+          markup += '<inlinemediaobject><imageobject><imagedata fileref="' + element['»'] + 
'"></imagedata></imageobject>'
 
-          if (defined ($element{"#"})) {
+          if 'a' in element:
+            markup += "<textobject><phrase>" + element["a"] + "</phrase></textobject>"
+
+
+            markup += "</inlinemediaobject>"
+        elif element["ref"]:
+          element["a"] = MarkDownParseSpanElementsInner(element["a"], markers_rest)
+          markup += '<link linkend="' + element['ref'] + '"'
+
+          if '#' in element["#"]:
             # title attribute not supported
-          }
 
-          $markup .= ">" . $element{"a"} . "</ulink>";
-        }
-      } else {
-        $markup .= $closest_marker;
-        if ($closest_marker eq "![") {
-          $offset = 2;
-        } else {
-          $offset = 1;
-        }
-      }
-    } elsif ($closest_marker eq "<") {
-      if ($text =~ /^<(https?:[\/]{2}[^\s]+?)>/i) {
-        my $element_url = $1;
-        $element_url =~ s/&/&amp;/g;
-        $element_url =~ s/</&lt;/g;
-
-        $markup .= "<ulink url=\"" . $element_url . "\">" . $element_url . "</ulink>";
-        $offset = length ($&);
-      } elsif ($text =~ /^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>/) {
-        $markup .= "<ulink url=\"mailto:"; . $1 . "\">" . $1 . "</ulink>";
-        $offset = length ($&);
-      } elsif ($text =~ /^<[^>]+?>/) {
-        $markup .= $&;
-        $offset = length ($&);
-      } else {
-        $markup .= "&lt;";
-        $offset = 1;
-      }
-    } elsif ($closest_marker eq "\\") {
-      my $special_char = substr ($text, 1, 1);
-      if ($MD_ESCAPABLE_CHARS{$special_char} ||
-          $MD_GTK_ESCAPABLE_CHARS{$special_char}) {
-        $markup .= $special_char;
-        $offset = 2;
-      } else {
-        $markup .= "\\";
-        $offset = 1;
-      }
-    } elsif ($closest_marker eq "`") {
-      if ($text =~ /^(`+)([^`]+?)\1(?!`)/) {
-        my $element_text = $2;
-        $markup .= "<literal>" . $element_text . "</literal>";
-        $offset = length ($&);
-      } else {
-        $markup .= "`";
-        $offset = 1;
-      }
-    } elsif ($closest_marker eq "@") {
+
+            markup += ">" + element["a"] + "</link>"
+        else:
+          element["a"] = MarkDownParseSpanElementsInner(element["a"], markers_rest)
+          markup += '<ulink url="' + element['»'] + '"'
+
+          if '#' in element:
+            # title attribute not supported
+            markup += ">" + element["a"] + "</ulink>"
+
+      else:
+        markup += closest_marker
+        if closest_marker == "![":
+          offset = 2
+        else:
+          offset = 1
+
+
+    elif closest_marker == "<":
+      m4 = re.search(r'^<(https?:[\/]{2}[^\s]+?)>', text)
+      m5 = re.search(r'^<([A-Za-z0-9._-]+?@[A-Za-z0-9._-]+?)>', text)
+      m6 = re.search(r'^<[^>]+?>', text)
+      if m4:
+        element_url = m4.group(1).replace('&', '&amp;').replace('<', '&lt;')
+
+        markup += '<ulink url="' + element_url + '">' + element_url + '</ulink>'
+        offset = len(m4.group(0))
+      elif m5:
+        markup += "<ulink url=\"mailto:"; + m5.group(1) + "\">" + m5.group(1) + "</ulink>"
+        offset = len(m5.group(0))
+      elif m6:
+        markup += m6.group(0)
+        offset = len(m6.group(0))
+      else:
+        markup += "&lt;"
+        offset = 1
+
+    elif closest_marker == "\\":
+      special_char = text[1]
+      if MD_ESCAPABLE_CHARS[special_char] or \
+          MD_GTK_ESCAPABLE_CHARS[special_char]:
+        markup += special_char
+        offset = 2
+      else:
+        markup += "\\"
+        offset = 1
+
+    elif closest_marker == "`":
+      m7 = re.search(r'^(`+)([^`]+?)\1(?!`)', text)
+      if m7:
+        element_text = m7.group(2)
+        markup += "<literal>" + element_text + "</literal>"
+        offset = len(m7.group(0))
+      else:
+        markup += "`"
+        offset = 1
+
+    elif closest_marker == "@":
       # Convert '@param()'
       # FIXME: we could make those also links ($symbol.$2), but that would be less
       # useful as the link target is a few lines up or down
-      if ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)/) {
-        $markup .= $1 . "<parameter>" . $2 . "()</parameter>\n";
-        $offset = length ($&);
-      } elsif ($text =~ /^(\A|[^\\])\@(\w+((\.|->)\w+)*)/) {
+      m7 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)\s*\(\)', text)
+      m8 = re.search(r'^(\A|[^\\])\@(\w+((\.|->)\w+)*)', text)
+      m9 = re.search(r'^\\\@', text)
+      if m7:
+        markup += m7.group(1) + "<parameter>" + m7.group(2) + "()</parameter>\n"
+        offset = len(m7.group(0))
+      elif m8:
         # Convert '@param', but not '\@param'.
-        $markup .= $1 . "<parameter>" . $2 . "</parameter>\n";
-        $offset = length ($&);
-      } elsif ($text =~ /^\\\@/) {
-        $markup .= "\@";
-        $offset = length ($&);
-      } else {
-        $markup .= "@";
-        $offset = 1;
-      }
-    } elsif ($closest_marker eq "#") {
-      if ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)/) {
+        markup += m8.group(1) + "<parameter>" + m8.group(2) + "</parameter>\n"
+        offset = len(m8.group(0))
+      elif m9:
+        markup += r"\@"
+        offset = len(m9.group(0))
+      else:
+        markup += "@"
+        offset = 1
+
+    elif closest_marker == "#":
+      m10 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)\s*\(\)', text)
+      m11 = re.search(r'^(\A|[^\\])#([\w\-:\.]+[\w]+)', text)
+      m12 = re.search(r'^\\#', text)
+      if m10:
         # handle #Object.func()
-        $markup .= $1 . &MakeXRef ($2, &tagify ($2 . "()", "function"));
-        $offset = length ($&);
-      } elsif ($text =~ /^(\A|[^\\])#([\w\-:\.]+[\w]+)/) {
+        markup += m10.group(1) + MakeXRef(m10.group(2), tagify(m10.group(2) + "()", "function"))
+        offset = len(m10.group(0))
+      elif m11:
         # Convert '#symbol', but not '\#symbol'.
-        $markup .= $1 . &MakeHashXRef ($2, "type");
-        $offset = length ($&);
-      } elsif ($text =~ /^\\#/) {
-        $markup .= "#";
-        $offset = length ($&);
-      } else {
-        $markup .= "#";
-        $offset = 1;
-      }
-    } elsif ($closest_marker eq "%") {
-      if ($text =~ /^(\A|[^\\])\%(-?\w+)/) {
+        markup += m11.group(1) + MakeHashXRef(m11.group(2), "type")
+        offset = len(m11.group(0))
+      elif m12:
+        markup += "#"
+        offset = len(m12.group(0))
+      else:
+        markup += "#"
+        offset = 1
+
+    elif closest_marker == "%":
+      m12 = re.search(r'^(\A|[^\\])\%(-?\w+)', text)
+      m13 = re.search(r'^\\%', text)
+      if m12:
         # Convert '%constant', but not '\%constant'.
         # Also allow negative numbers, e.g. %-1.
-        $markup .= $1 . &MakeXRef ($2, &tagify ($2, "literal"));
-        $offset = length ($&);
-      } elsif ($text =~ /^\\%/) {
-        $markup .= "\%";
-        $offset = length ($&);
-      } else {
-        $markup .= "%";
-        $offset = 1;
-      }
-    }
-
-    if ($offset > 0) {
-      $text = substr ($text, $offset);
-    }
-  }
-
-  return $markup;
-}
+        markup += m12.group(1) + MakeXRef(m12.group(2), tagify(m12.group(2), "literal"))
+        offset = len(m12.group(0))
+      elif m13:
+        markup += r"\%"
+        offset = len(m13.group(0))
+      else:
+        markup += "%"
+        offset = 1
 
-sub MarkDownParseSpanElements {
-  my ($text) = @_;
-  my @markers = ( "\\", "<", "![", "[", "`", "%", "#", "@" );
 
-  $text = &MarkDownParseSpanElementsInner ($text, \@markers);
 
-  # Convert 'function()' or 'macro()'.
-  # if there is abc_*_def() we don't want to make a link to _def()
-  # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
-  $text =~ s/([^\*.\w])(\w+)\s*\(\)/$1.&MakeXRef($2, &tagify($2 . "()", "function"));/eg;
+    if offset > 0:
+      text = text[offset:]
 
-  return $text;
-}
+  return markup
 
-sub ReplaceEntities {
-  my ($text, $symbol) = @_;
-  my $warn = "";
-  my @entities = ( [ "&lt;", "<" ],
-                   [ "&gt;", ">" ],
-                   [ "&ast;", "*" ],
-                   [ "&num;", "#" ],
-                   [ "&percnt;", "%"],
-                   [ "&colon;", ":" ],
-                   [ "&quot;", "\"" ],
-                   [ "&apos;", "'" ],
-                   [ "&nbsp;", " " ],
-                   [ "&amp;", "&" ] ); # Do this last, or the others get messed up.
-  my $i;
-
-  # Expand entities in <programlisting> even inside CDATA since
-  # we changed the definition of |[ to add CDATA
-  for ($i = 0; $i <= $#entities; $i++) {
-    $text =~ s/$entities[$i][0]/$entities[$i][1]/g;
-  }
-
-  return $text;
-}
 
-sub MarkDownOutputDocBook {
-  my ($blocksref, $symbol, $context) = @_;
-  my $output = "";
-  my $block;
-  my @blocks = @$blocksref;
-
-  foreach $block (@blocks) {
-    my $text;
-    my $title;
-
-    #$output .= "\n<!-- beg type='" . $block->{"type"} . "'-->\n";
-
-    if ($block->{"type"} eq "paragraph") {
-      $text = &MarkDownParseSpanElements ($block->{"text"});
-      if ($context eq "li" && $output eq "") {
-        if ($block->{"interrupted"}) {
-          $output .= "\n<para>$text</para>\n";
-        } else {
-          $output .= "<para>$text</para>";
-          if ($#blocks > 0) {
-            $output .= "\n";
-          }
-        }
-      } else {
-        $output .= "<para>$text</para>\n";
-      }
-
-    } elsif ($block->{"type"} eq "heading") {
-      my $tag;
-
-      $title = &MarkDownParseSpanElements ($block->{"text"});
-
-      if ($block->{"level"} == 1) {
-        $tag = "refsect2";
-      } else {
-        $tag = "refsect3";
-      }
-
-      $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "heading");
-      if (defined ($block->{"id"})) {
-        $output .= "<$tag id=\"" . $block->{"id"} . "\">";
-      } else {
-        $output .= "<$tag>";
-      }
-
-      $output .= "<title>$title</title>$text</$tag>\n";
-    } elsif ($block->{"type"} eq "li") {
-      my $tag = "itemizedlist";
-
-      if ($block->{"first"}) {
-        if ($block->{"ordered"}) {
-          $tag = "orderedlist";
-        }
-        $output .= "<$tag>\n";
-      }
-
-      if ($block->{"interrupted"}) {
-        push @{$block->{"lines"}}, "";
-      }
-
-      $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "li");
-      $output .= "<listitem>".$text."</listitem>\n";
-      if ($block->{"last"}) {
-        if ($block->{"ordered"}) {
-          $tag = "orderedlist";
-        }
-        $output .= "</$tag>\n";
-      }
-    } elsif ($block->{"type"} eq "quote") {
-      $text = &MarkDownParseLines ($block->{"lines"}, $symbol, "quote");
-      $output .= "<blockquote>\n$text</blockquote>\n";
-    } elsif ($block->{"type"} eq "code") {
-      my $tag = "programlisting";
-
-      if ($block->{"language"}) {
-        if ($block->{"language"} eq "plain") {
-          $output .= "<informalexample><screen><![CDATA[\n";
-          $tag = "screen";
-        } else {
-          $output .= "<informalexample><programlisting language=\"" . $block->{"language"} . 
"\"><![CDATA[\n";
-        }
-      } else {
-        $output .= "<informalexample><programlisting><![CDATA[\n";
-      }
-      foreach (@{$block->{"lines"}}) {
-        $output .= &ReplaceEntities ($_, $symbol) . "\n";
-      }
-      $output .= "]]></$tag></informalexample>\n";
-    } elsif ($block->{"type"} eq "markup") {
-      $text = &ExpandAbbreviations($symbol, $block->{"text"});
-      $output .= $text."\n";
-    } else {
-      $output .= $block->{"text"}."\n";
-    }
-    #$output .= "\n<!-- end type='" . $block->{"type"} . "'-->\n";
-  }
-
-  return $output;
-}
+def MarkDownParseSpanElements(text):
+    markers = ["\\", "<", "![", "[", "`", "%", "#", "@"]
 
-sub MarkDownParseLines {
-  my ($linesref, $symbol, $context) = @_;
-  my $output;
-  my @lines = @$linesref;
-  my @blocks;
+    text = MarkDownParseSpanElementsInner(text, markers)
 
-  @blocks = &MarkDownParseBlocks (\@lines, $symbol, $context);
-  $output = &MarkDownOutputDocBook (\@blocks, $symbol, $context);
+    # Convert 'function()' or 'macro()'.
+    # if there is abc_*_def() we don't want to make a link to _def()
+    # FIXME: also handle abc(def(....)) : but that would need to be done recursively :/
+    def f(m):
+        return m.group(1) + MakeXRef(m.group(2), tagify(m.group(2) + "()", "function")) + ';'
+    text = re.sub(r'([^\*.\w])(\w+)\s*\(\)', f, text)
+    return text
 
-  return $output;
-}
 
-sub MarkDownParse {
-  my ($text, $symbol) = @_;
-  my @lines;
+def ReplaceEntities(text, symbol):
+    entities = [["&lt;", "<"],
+                ["&gt;", ">"],
+                ["&ast;", "*"],
+                ["&num;", "#"],
+                ["&percnt;", "%"],
+                ["&colon;", ":"],
+                ["&quot;", '"'],
+                ["&apos;", "'"],
+                ["&nbsp;", " "],
+                ["&amp;", "&"], # Do this last, or the others get messed up.
+               ]
 
-  # take out some variability in line endings
-  $text =~ s%\r\n%\n%g;
-  $text =~ s%\r%\n%g;
 
-  # split lines
-  @lines = split("\n", $text);
-  $text = MarkDownParseLines(\@lines, $symbol, "");
+    # Expand entities in <programlisting> even inside CDATA since
+    # we changed the definition of |[ to add CDATA
+    for i in entities:
+        text = re.sub(i[0], i[1], text)
+    return text
+
+
+def MarkDownOutputDocBook(blocksref, symbol, context):
+  output = ''
+  blocks = blocksref
+
+  for block in blocks:
+    #$output += "\n<!-- beg type='" . $block->{"type"} . "'-->\n"
+
+    if block["type"] == "paragraph":
+      text = MarkDownParseSpanElements(block["text"])
+      if context == "li" and output == '':
+        if block["interrupted"]:
+          output += "\n<para>%s</para>\n" % text
+        else:
+          output += "<para>%s</para>" % text
+          if len(blocks) > 0:
+            output += "\n"
+      else:
+        output += "<para>%s</para>\n" % text
+
+    elif block["type"] == "heading":
+
+      title = MarkDownParseSpanElements(block["text"])
+
+      if block["level"] == 1:
+        tag = "refsect2"
+      else:
+        tag = "refsect3"
+
+
+      text = MarkDownParseLines(block["lines"], symbol, "heading")
+      if id in block:
+        output += "<%s id=\"%s\">" % (tag, block["id"])
+      else:
+        output += "<%s>" % tag
+
+
+      output += "<title>%s</title>%s</%s>\n" % (title, text, tag)
+    elif block["type"] == "li":
+      tag = "itemizedlist"
+
+      if block["first"]:
+        if block["ordered"]:
+          tag = "orderedlist"
+
+        output += "<%s>\n" % tag
+
+
+      if block["interrupted"]:
+        block["lines"].append('')
+
+
+      text = MarkDownParseLines(block["lines"], symbol, "li")
+      output += "<listitem>" + text + "</listitem>\n"
+      if block["last"]:
+        if block["ordered"]:
+          tag = "orderedlist"
+        output += "</$tag>\n"
+
+    elif block["type"] == "quote":
+      text = MarkDownParseLines(block["lines"], symbol, "quote")
+      output += "<blockquote>\n%s</blockquote>\n" % text
+    elif block["type"] == "code":
+      tag = "programlisting"
+
+      if block["language"]:
+        if block["language"] == "plain":
+          output += "<informalexample><screen><![CDATA[\n"
+          tag = "screen"
+        else:
+          output += '<informalexample><programlisting language="' + block['language'] + '"><![CDATA[\n'
+
+      else:
+        output += "<informalexample><programlisting><![CDATA[\n"
+
+      for line in block["lines"]:
+        output += ReplaceEntities(line, symbol) + "\n"
+
+      output += "]]></$tag></informalexample>\n"
+    elif block["type"] == "markup":
+      text = ExpandAbbreviations(symbol, block["text"])
+      output += text+"\n"
+    else:
+      output += block["text"]+"\n"
+
+    #$output += "\n<!-- end type='" . $block->{"type"} . "'-->\n"
+
+  return output
+
+
+def MarkDownParseLines(linesref, symbol, context):
+    lines = linesref
+
+    blocks = MarkDownParseBlocks(lines, symbol, context)
+    output = MarkDownOutputDocBook(blocks, symbol, context)
+
+    return output
+
+
+def MarkDownParse(text, symbol):
+    # take out some variability in line endings
+    text = re.sub(r'\r\n', '\n', text)
+    text = re.sub(r'\r', '\n', text)
+
+    # split lines
+    lines = text.split('\n')
+    text = MarkDownParseLines(lines, symbol, '')
+
+    return text
 
-  return $text;
-}
 
 #############################################################################
 # Function    : ReadDeclarationsFile
@@ -5734,102 +5314,102 @@ sub MarkDownParse {
 #                        any current declaration.
 #############################################################################
 
-sub ReadDeclarationsFile {
-    my ($file, $override) = @_;
-
-    if ($override == 0) {
-        %Declarations = ();
-        %DeclarationTypes = ();
-        %DeclarationConditional = ();
-        %DeclarationOutput = ();
-    }
-
-    open (INPUT, $file)
-        || die "Can't open $file: $!";
-    my $declaration_type = "";
-    my $declaration_name;
-    my $declaration;
-    my $is_deprecated = 0;
-    while (<INPUT>) {
-        if (!$declaration_type) {
-            if (m/^<([^>]+)>/) {
-                $declaration_type = $1;
-                $declaration_name = "";
-                @TRACE@("Found declaration: $declaration_type\n");
-                $declaration = "";
-            }
-        } else {
-            if (m%^<NAME>(.*)</NAME>%) {
-                $declaration_name = $1;
-            } elsif (m%^<DEPRECATED/>%) {
-                $is_deprecated = 1;
-            } elsif (m%^</$declaration_type>%) {
-                @TRACE@("Found end of declaration: $declaration_name\n");
+def ReadDeclarationsFile(ifile, override):
+
+    if override == 0:
+        global Declarations, DeclarationTypes, DeclarationConditional, DeclarationOutput
+        Declarations = {}
+        DeclarationTypes = {}
+        DeclarationConditional = {}
+        DeclarationOutput = {}
+
+
+    INPUT = open(ifile)
+    declaration_type = ''
+    declaration_name = None
+    declaration = None
+    is_deprecated = 0
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not declaration_type:
+            m1 = re.search(r'^<([^>]+)>', line)
+            if m1:
+                declaration_type = m1.group(1)
+                declaration_name = ''
+                logging.info("Found declaration: %s", declaration_type)
+                declaration = ''
+
+        else:
+            m2 = re.search(r'^<NAME>(.*)</NAME>', line)
+            m3 = re.search(r'^<DEPRECATED/>', line)
+            m4 = re.search(r'^</$declaration_type>', line)
+            if m2:
+                declaration_name = m2.group(1)
+            elif m3:
+                is_deprecated = True
+            elif m4:
+                logging.info("Found end of declaration: %s", declaration_name)
                 # Check that the declaration has a name
-                if ($declaration_name eq "") {
-                    &LogWarning ($file, $., "$declaration_type has no name.\n");
-                }
+                if declaration_name == '':
+                    common.LogWarning(ifile, line_number, declaration_type + " has no name.\n")
+
 
                 # If the declaration is an empty typedef struct _XXX XXX
                 # set the flag to indicate the struct has a typedef.
-                if (($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION')
-                    && $declaration =~ m/^\s*$/) {
-                    @TRACE@("Struct has typedef: $declaration_name\n");
-                    $StructHasTypedef{$declaration_name} = 1;
-                }
+                if declaration_type == 'STRUCT' or declaration_type == 'UNION' \
+                    and re.search(r'^\s*$', declaration):
+                    logging.info("Struct has typedef: " + declaration_name)
+                    StructHasTypedef[declaration_name] = 1
+
 
                 # Check if the symbol is already defined.
-                if (defined ($Declarations{$declaration_name})
-                    && $override == 0) {
+                if declaration_name in Declarations and override == 0:
                     # Function declarations take precedence.
-                    if ($DeclarationTypes{$declaration_name} eq 'FUNCTION') {
+                    if DeclarationTypes[declaration_name] == 'FUNCTION':
                         # Ignore it.
-                    } elsif ($declaration_type eq 'FUNCTION') {
-                        if ($is_deprecated) {
-                            $Deprecated{$declaration_name} = "";
-                        }
-                        $Declarations{$declaration_name} = $declaration;
-                        $DeclarationTypes{$declaration_name} = $declaration_type;
-                    } elsif ($DeclarationTypes{$declaration_name}
-                              eq $declaration_type) {
+                        pass
+                    elif declaration_type == 'FUNCTION':
+                        if is_deprecated:
+                            Deprecated[declaration_name] = ''
+
+                        Declarations[declaration_name] = declaration
+                        DeclarationTypes[declaration_name] = declaration_type
+                    elif DeclarationTypes[declaration_name] == declaration_type:
                         # If the existing declaration is empty, or is just a
                         # forward declaration of a struct, override it.
-                        if ($declaration_type eq 'STRUCT' || $declaration_type eq 'UNION') {
-                            if ($Declarations{$declaration_name} =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
-                                if ($is_deprecated) {
-                                    $Deprecated{$declaration_name} = "";
-                                }
-                                $Declarations{$declaration_name} = $declaration;
-                            } elsif ($declaration =~ m/^\s*((struct|union)\s+\w+\s*;)?\s*$/) {
+                        if declaration_type == 'STRUCT' or declaration_type == 'UNION':
+                            if re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', 
Declarations[declaration_name]):
+                                if is_deprecated:
+                                    Deprecated[declaration_name] = ''
+                                Declarations[declaration_name] = declaration
+                            elif re.search(r'^\s*((struct|union)\s+\w+\s*;)?\s*$', declaration):
                                 # Ignore an empty or forward declaration.
-                            } else {
-                                &LogWarning ($file, $., "Structure $declaration_name has multiple 
definitions.");
-                            }
-                        } else {
+                                pass
+                            else:
+                                common.LogWarning(ifile, line_number, "Structure %s has multiple 
definitions." % declaration_name)
+
+                        else:
                             # set flag in %DeclarationConditional hash for
                             # multiply defined macros/typedefs.
-                            $DeclarationConditional{$declaration_name} = 1;
-                        }
-                    } else {
-                        &LogWarning ($file, $., "$declaration_name has multiple definitions.");
-                    }
-                } else {
-                    if ($is_deprecated) {
-                        $Deprecated{$declaration_name} = "";
-                    }
-                    $Declarations{$declaration_name} = $declaration;
-                    $DeclarationTypes{$declaration_name} = $declaration_type;
-                }
-
-                $declaration_type = "";
-                $is_deprecated = 0;
-            } else {
-                $declaration .= $_;
-            }
-        }
-    }
-    close (INPUT);
-}
+                            DeclarationConditional[declaration_name] = 1
+
+                    else:
+                        common.LogWarning(ifile, line_number, declaration_name + " has multiple 
definitions.")
+
+                else:
+                    if is_deprecated:
+                        Deprecated[declaration_name] = ''
+
+                    Declarations[declaration_name] = declaration
+                    DeclarationTypes[declaration_name] = declaration_type
+
+
+                declaration_type = ''
+                is_deprecated = False
+            else:
+                declaration += line
+    INPUT.close()
 
 
 #############################################################################
@@ -5845,68 +5425,68 @@ sub ReadDeclarationsFile {
 #                        information.
 #############################################################################
 
-sub ReadSignalsFile {
-    my ($file) = @_;
+def ReadSignalsFile(ifile):
 
-    my $in_signal = 0;
-    my $signal_object;
-    my $signal_name;
-    my $signal_returns;
-    my $signal_flags;
-    my $signal_prototype;
+    in_signal = 0
+    signal_object = None
+    signal_name = None
+    signal_returns = None
+    signal_flags = None
+    signal_prototype = None
 
     # Reset the signal info.
-    @SignalObjects = ();
-    @SignalNames = ();
-    @SignalReturns = ();
-    @SignalFlags = ();
-    @SignalPrototypes = ();
-
-    if (! -f $file) {
-        return;
-    }
-    if (!open (INPUT, $file)) {
-        warn "Can't open $file - skipping signals\n";
-        return;
-    }
-    while (<INPUT>) {
-        if (!$in_signal) {
-            if (m/^<SIGNAL>/) {
-                $in_signal = 1;
-                $signal_object = "";
-                $signal_name = "";
-                $signal_returns = "";
-                $signal_prototype = "";
-            }
-        } else {
-            if (m/^<NAME>(.*)<\/NAME>/) {
-                $signal_name = $1;
-                if ($signal_name =~ m/^(.*)::(.*)$/) {
-                    $signal_object = $1;
-                    ($signal_name = $2) =~ s/_/-/g;
-                    @TRACE@("Found signal: $signal_name\n");
-                } else {
-                    &LogWarning ($file, $., "Invalid signal name: $signal_name.");
-                }
-            } elsif (m/^<RETURNS>(.*)<\/RETURNS>/) {
-                $signal_returns = $1;
-            } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
-                $signal_flags = $1;
-            } elsif (m%^</SIGNAL>%) {
-                @TRACE@("Found end of signal: ${signal_object}::${signal_name}\nReturns: 
${signal_returns}\n${signal_prototype}");
-                push (@SignalObjects, $signal_object);
-                push (@SignalNames, $signal_name);
-                push (@SignalReturns, $signal_returns);
-                push (@SignalFlags, $signal_flags);
-                push (@SignalPrototypes, $signal_prototype);
-                $in_signal = 0;
-            } else {
-                $signal_prototype .= $_;
-            }
-        }
-    }
-    close (INPUT);
-}
+    global SignalObjects, SignalNames, SignalReturns, SignalFlags, SignalPrototypes
+    SignalObjects = []
+    SignalNames = []
+    SignalReturns = []
+    SignalFlags = []
+    SignalPrototypes = []
+
+    if not os.path.isfile(ifile):
+        return
+
+    INPUT = open(ifile)
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not in_signal:
+            if re.search(r'^<SIGNAL>', line):
+                in_signal = 1
+                signal_object = ''
+                signal_name = ''
+                signal_returns = ''
+                signal_prototype = ''
+
+        else:
+            m = re.search(r'^<NAME>(.*)<\/NAME>', line)
+            m2 = re.search(r'^<RETURNS>(.*)<\/RETURNS>', line)
+            m3 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
+            if m:
+                signal_name = m.group(1)
+                m1_2 = re.search(r'^(.*)::(.*)$', signal_name)
+                if m1_2:
+                    signal_object = m1_2.group(1)
+                    signal_name = m1_2.group(2).replace('_', '-')
+                    logging.info("Found signal: %s", signal_name)
+                else:
+                    common.LogWarning(ifile, line_number, "Invalid signal name: %s." % signal_name)
+
+            elif m2:
+                signal_returns = m2.group(1)
+            elif m3:
+                signal_flags = m3.group(1)
+            elif re.search(r'^</SIGNAL>', line):
+                logging.info("Found end of signal: %s::%s\nReturns: %s\n%s", signal_object, signal_name, 
signal_returns, signal_prototype)
+                SignalObjects.append(signal_object)
+                SignalNames.append(signal_name)
+                SignalReturns.append(signal_returns)
+                SignalFlags.append(signal_flags)
+                SignalPrototypes.append(signal_prototype)
+                in_signal = False
+            else:
+                signal_prototype += line
+    INPUT.close()
+
 
 
 #############################################################################
@@ -5921,179 +5501,173 @@ sub ReadSignalsFile {
 #                 skipped.
 #############################################################################
 
-sub ReadTemplateFile {
-    my ($docsfile, $skip_unused_params) = @_;
+def ReadTemplateFile(docsfile, skip_unused_params):
+
+    template = docsfile + ".sgml"
+    if not os.path.isfile(template):
+        logging.info("File doesn't exist: " + template)
+        return 0
 
-    my $template = "$docsfile.sgml";
-    if (! -f $template) {
-        @TRACE@("File doesn't exist: $template\n");
-        return 0;
-    }
 
     # start with empty hashes, we merge the source comment for each file
     # afterwards
-    %SymbolDocs = ();
-    %SymbolTypes = ();
-    %SymbolParams = ();
-
-    my $current_type = "";        # Type of symbol being read.
-    my $current_symbol = "";        # Name of symbol being read.
-    my $symbol_doc = "";                # Description of symbol being read.
-    my @params;                        # Parameter names and descriptions of current
+    global SymbolDocs, SymbolTypes, SymbolParams
+    SymbolDocs = {}
+    SymbolTypes = {}
+    SymbolParams = {}
+
+    current_type = ''        # Type of symbol being read.
+    current_symbol = ''        # Name of symbol being read.
+    symbol_doc = ''                # Description of symbol being read.
+    params = []                        # Parameter names and descriptions of current
                                 #   function/macro/function typedef.
-    my $current_param = -1;        # Index of parameter currently being read.
+    current_param = -1        # Index of parameter currently being read.
                                 #   Note that the param array contains pairs
                                 #   of param name & description.
-    my $in_unused_params = 0;        # True if we are reading in the unused params.
-    my $in_deprecated = 0;
-    my $in_since = 0;
-    my $in_stability = 0;
-
-    open (DOCS, "$template")
-        || die "Can't open $template: $!";
-
-    @TRACE@("reading template $template");
-
-    while (<DOCS>) {
-        if (m/^<!-- ##### ([A-Z_]+) (\S+) ##### -->/) {
-            my $type = $1;
-            my $symbol = $2;
-            if ($symbol eq "Title"
-                || $symbol eq "Short_Description"
-                || $symbol eq "Long_Description"
-                || $symbol eq "See_Also"
-                || $symbol eq "Stability_Level"
-                || $symbol eq "Include"
-                || $symbol eq "Image") {
-
-                $symbol = $docsfile . ":" . $symbol;
-            }
-
-            @TRACE@("Found symbol: $symbol\n");
+    in_unused_params = 0        # True if we are reading in the unused params.
+    in_deprecated = 0
+    in_since = 0
+    in_stability = 0
+
+    DOCS = open(template)
+
+    logging.info("reading template " + template)
+
+    line_number = 0
+    for line in DOCS:
+        line_number += 1
+        m1 = re.search(r'^<!-- ##### ([A-Z_]+) (\S+) ##### -->', line)
+        if m1:
+            stype = m1.group(1)
+            symbol = m1.group(2)
+            if symbol == "Title" \
+                or symbol == "Short_Description" \
+                or symbol == "Long_Description" \
+                or symbol == "See_Also" \
+                or symbol == "Stability_Level" \
+                or symbol == "Include" \
+                or symbol == "Image":
+
+                symbol = docsfile + ":" + symbol
+
+
+            logging.info("Found symbol: " + symbol)
             # Remember file and line for the symbol
-            $SymbolSourceFile{$symbol} = $template;
-            $SymbolSourceLine{$symbol} = $.;
+            SymbolSourceFile[symbol] = template
+            SymbolSourceLine[symbol] = line_number
 
             # Store previous symbol, but remove any trailing blank lines.
-            if ($current_symbol ne "") {
-                $symbol_doc =~ s/\s+$//;
-                $SymbolTypes{$current_symbol} = $current_type;
-                $SymbolDocs{$current_symbol} = $symbol_doc;
+            if current_symbol != '':
+                symbol_doc = symbol_doc.rstrip()
+                SymbolTypes[current_symbol] = current_type
+                SymbolDocs[current_symbol] = symbol_doc
 
                 # Check that the stability level is valid.
-                if ($StabilityLevel{$current_symbol}) {
-                    $StabilityLevel{$current_symbol} = 
&ParseStabilityLevel($StabilityLevel{$current_symbol}, $template, $., "Stability level for $current_symbol");
-                }
+                if StabilityLevel[current_symbol]:
+                    StabilityLevel[current_symbol] = ParseStabilityLevel(StabilityLevel[current_symbol], 
template, line_number, "Stability level for " + current_symbol)
 
-                if ($current_param >= 0) {
-                    $SymbolParams{$current_symbol} = [ @params ];
-                } else {
+                if current_param >= 0:
+                    SymbolParams[current_symbol] = [params]
+                else:
                     # Delete any existing params in case we are overriding a
                     # previously read template.
-                    delete $SymbolParams{$current_symbol};
-                }
-            }
-            $current_type = $type;
-            $current_symbol = $symbol;
-            $current_param = -1;
-            $in_unused_params = 0;
-            $in_deprecated = 0;
-            $in_since = 0;
-            $in_stability = 0;
-            $symbol_doc = "";
-            @params = ();
-
-        } elsif (m/^<!-- # Unused Parameters # -->/) {
-            @TRACE@("Found unused parameters\n");
-            $in_unused_params = 1;
-            next;
-
-        } elsif ($in_unused_params && $skip_unused_params) {
+                    del SymbolParams[current_symbol]
+
+
+            current_type = stype
+            current_symbol = symbol
+            current_param = -1
+            in_unused_params = 0
+            in_deprecated = 0
+            in_since = 0
+            in_stability = 0
+            symbol_doc = ''
+            params = []
+
+        elif re.search(r'^<!-- # Unused Parameters # -->', line):
+            logging.info("Found unused parameters\n")
+            in_unused_params = True
+            continue
+
+        elif in_unused_params and skip_unused_params:
             # When outputting the DocBook we skip unused parameters.
-            @TRACE@("Skipping unused param: $_");
-            next;
+            logging.info("Skipping unused param: " + line)
+            continue
 
-        } else {
+        else:
             # Check if param found. Need to handle "..." and "format...".
-            if (s/^\@([\w\.]+):\040?//) {
-                my $param_name = $1;
-                my $param_desc = $_;
+            m2 = re.search(r'^\@([\w\.]+):\040?', line)
+            if m2:
+                line = re.sub(r'^\@([\w\.]+):\040?', '', line)
+                param_name = m2.group(1)
+                param_desc = line
                 # Allow variations of 'Returns'
-                if ($param_name =~ m/^[Rr]eturns?$/) {
-                    $param_name = "Returns";
-                }
+                if re.search(r'^[Rr]eturns?$', param_name):
+                    param_name = "Returns"
+
                 # Allow varargs variations
-                if ($param_name =~ m/^.*\.\.\.$/) {
-                    $param_name = "...";
-                }
+                if re.search(r'^.*\.\.\.$', param_name):
+                    param_name = "..."
+
 
                 # strip trailing whitespaces and blank lines
-                s/\s+\n$/\n/m;
-                s/\n+$/\n/sm;
-                @TRACE@("Found param for symbol $current_symbol : '$param_name'= '$_'");
-
-                if ($param_name eq "Deprecated") {
-                    $in_deprecated = 1;
-                    $Deprecated{$current_symbol} = $_;
-                } elsif ($param_name eq "Since") {
-                    $in_since = 1;
-                    chomp;
-                    $Since{$current_symbol} = $_;
-                } elsif ($param_name eq "Stability") {
-                    $in_stability = 1;
-                    $StabilityLevel{$current_symbol} = $_;
-                } else {
-                    push (@params, $param_name);
-                    push (@params, $param_desc);
-                    $current_param += $PARAM_FIELD_COUNT;
-                }
-            } else {
+                line = re.sub(r'\s+\n$', '\n', line, flags=re.M)
+                line = re.sub(r'\n+$', '\n', line, flags=re.M|re.S)
+                logging.info("Found param for symbol %s : '%s'= '%s'", current_symbol, param_name, line)
+
+                if param_name == "Deprecated":
+                    in_deprecated = True
+                    Deprecated[current_symbol] = line
+                elif param_name == "Since":
+                    in_since = True
+                    Since[current_symbol] = line.strip()
+                elif param_name == "Stability":
+                    in_stability = True
+                    StabilityLevel[current_symbol] = line
+                else:
+                    params.append(param_name)
+                    params.append(param_desc)
+                    current_param += PARAM_FIELD_COUNT
+
+            else:
                 # strip trailing whitespaces and blank lines
-                s/\s+\n$/\n/m;
-                s/\n+$/\n/sm;
-
-                if (!m/^\s+$/) {
-                    if ($in_deprecated) {
-                        $Deprecated{$current_symbol} .= $_;
-                    } elsif ($in_since) {
-                        &LogWarning ($template, $., "multi-line since docs found");
-                        #$Since{$current_symbol} .= $_;
-                    } elsif ($in_stability) {
-                        $StabilityLevel{$current_symbol} .= $_;
-                    } elsif ($current_param >= 0) {
-                        $params[$current_param] .= $_;
-                    } else {
-                        $symbol_doc .= $_;
-                    }
-                }
-            }
-        }
-    }
+                line = re.sub(r'\s+\n$', '\n', line, flags=re.M)
+                line = re.sub(r'\n+$', '\n', line, flags=re.M|re.S)
+
+                if re.search(r'^\s+$', line):
+                    if in_deprecated:
+                        Deprecated[current_symbol] += line
+                    elif in_since:
+                        common.LogWarning(template, line_number, "multi-line since docs found")
+                        #$Since{$current_symbol} += $_
+                    elif in_stability:
+                        StabilityLevel[current_symbol] += line
+                    elif current_param >= 0:
+                        params[current_param] += line
+                    else:
+                        symbol_doc += line
 
     # Remember to finish the current symbol doccs.
-    if ($current_symbol ne "") {
+    if current_symbol != '':
 
-        $symbol_doc =~ s/\s+$//;
-        $SymbolTypes{$current_symbol} = $current_type;
-        $SymbolDocs{$current_symbol} = $symbol_doc;
+        symbol_doc = re.sub(r'\s+$', '', symbol_doc)
+        SymbolTypes[current_symbol] = current_type
+        SymbolDocs[current_symbol] = symbol_doc
 
         # Check that the stability level is valid.
-        if ($StabilityLevel{$current_symbol}) {
-            $StabilityLevel{$current_symbol} = &ParseStabilityLevel($StabilityLevel{$current_symbol}, 
$template, $., "Stability level for $current_symbol");
-        }
+        if StabilityLevel[current_symbol]:
+            StabilityLevel[current_symbol] = ParseStabilityLevel(StabilityLevel[current_symbol], template, 
line_number, "Stability level for " + current_symbol)
 
-        if ($current_param >= 0) {
-            $SymbolParams{$current_symbol} = [ @params ];
-        } else {
+        if current_param >= 0:
+            SymbolParams[current_symbol] = [params]
+        else:
             # Delete any existing params in case we are overriding a
             # previously read template.
-            delete $SymbolParams{$current_symbol};
-        }
-    }
+            del SymbolParams[current_symbol]
+
+    DOCS.close()
+    return 1
 
-    close (DOCS);
-    return 1;
-}
 
 
 #############################################################################
@@ -6110,78 +5684,79 @@ sub ReadTemplateFile {
 # Arguments   : none
 #############################################################################
 
-sub ReadObjectHierarchy {
-    @Objects = ();
-    @ObjectLevels = ();
+def ReadObjectHierarchy():
+
+    global Objects, ObjectLevels
+    Objects = []
+    ObjectLevels = []
 
-    if (! -f $OBJECT_TREE_FILE) {
-        return;
-    }
-    if (!open (INPUT, $OBJECT_TREE_FILE)) {
-        warn "Can't open $OBJECT_TREE_FILE - skipping object tree\n";
-        return;
-    }
+    if not os.path.isfile(OBJECT_TREE_FILE):
+        return
+
+    INPUT = open(OBJECT_TREE_FILE)
 
     # Only emit objects if they are supposed to be documented, or if
     # they have documented children. To implement this, we maintain a
     # stack of pending objects which will be emitted if a documented
     # child turns up.
-    my @pending_objects = ();
-    my @pending_levels = ();
-    my $root;
-    my @tree = ();
-    while (<INPUT>) {
-        if (m/\S+/) {
-            my $object = $&;
-            my $level = (length($`)) / 2 + 1;
-            my $xref = "";
-
-            if ($level == 1) {
-                $root = $object;
-            }
-
-            while (($#pending_levels >= 0) && ($pending_levels[$#pending_levels] >= $level)) {
-                my $pobject = pop(@pending_objects);
-                my $plevel = pop(@pending_levels);
-            }
-
-            push (@pending_objects, $object);
-            push (@pending_levels, $level);
-
-            if (exists($KnownSymbols{$object})) {
-                while ($#pending_levels >= 0) {
-                    $object = shift @pending_objects;
-                    $level = shift @pending_levels;
-                    $xref = &MakeXRef ($object);
-
-                    push (@tree, ' ' x ($level * 4) . "$xref");
-                    push (@Objects, $object);
-                    push (@ObjectLevels, $level);
-                    $ObjectRoots{$object} = $root;
-                }
-            }
-            #else {
-            #    LogWarning ($OBJECT_TREE_FILE, $., "unknown type $object");
-            #}
-        }
-    }
-    close (INPUT);
+    pending_objects = []
+    pending_levels = []
+    root = None
+    tree = []
+    for line in INPUT:
+        m1 = re.search(r'\S+', line)
+        if m1:
+            gobject = m1.group(0)
+            level = len(line[0:m1.start()]) / 2 + 1
+            xref = ''
+
+            if level == 1:
+                root = gobject
+
+
+            while pending_levels and pending_levels[-1] >= level:
+                pending_objects.pop()
+                pending_levels.pop()
+
+
+            pending_objects.append(gobject)
+            pending_levels.append(level)
+
+            if gobject in KnownSymbols:
+                while len(pending_levels) > 0:
+                    gobject = pending_objects[0]
+                    pending_objects = pending_objects[1:]
+                    level = pending_levels[0]
+                    pending_levels = pending_levels[1:]
+                    xref = MakeXRef(gobject)
+
+                    tree.append(' ' * (level * 4) + xref)
+                    Objects.append(gobject)
+                    ObjectLevels.append(level)
+                    ObjectRoots[gobject] = root
+
+
+            #else
+            #    common.LogWarning($OBJECT_TREE_FILE, line_number, "unknown type $object")
+            #
+
+
+    INPUT.close()
 
     # FIXME: use xml
-    # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml";
-    my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.sgml";
-    my $new_tree_index = "$DB_OUTPUT_DIR/tree_index.new";
+    # my $old_tree_index = "$DB_OUTPUT_DIR/tree_index.$xml"
+    old_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.sgml")
+    new_tree_index = os.path.join(DB_OUTPUT_DIR, "tree_index.new")
 
-    open (OUTPUT, ">$new_tree_index")
-        || die "Can't create $new_tree_index: $!";
+    OUTPUT = open(new_tree_index, 'w')
 
-    print (OUTPUT &MakeDocHeader ("screen")."\n<screen>\n".&AddTreeLineArt(\@tree)."\n</screen>\n");
-    close (OUTPUT);
+    OUTPUT.write(MakeDocHeader("screen") + "\n<screen>\n" + AddTreeLineArt(tree) + "\n</screen>\n")
+    OUTPUT.close()
 
-    &UpdateFileIfChanged ($old_tree_index, $new_tree_index, 0);
+    common.UpdateFileIfChanged(old_tree_index, new_tree_index, 0)
+
+    OutputObjectList()
 
-    &OutputObjectList;
-}
 
 #############################################################################
 # Function    : ReadInterfaces
@@ -6190,38 +5765,35 @@ sub ReadObjectHierarchy {
 # Arguments   : none
 #############################################################################
 
-sub ReadInterfaces {
-    %Interfaces = ();
-
-    if (! -f $INTERFACES_FILE) {
-        return;
-    }
-    if (!open (INPUT, $INTERFACES_FILE)) {
-        warn "Can't open $INTERFACES_FILE - skipping interfaces\n";
-        return;
-    }
-
-    while (<INPUT>) {
-       chomp;
-       my ($object, @ifaces) = split;
-       if (exists($KnownSymbols{$object}) && $KnownSymbols{$object} == 1) {
-           my @knownIfaces = ();
-
-           # filter out private interfaces, but leave foreign interfaces
-           foreach my $iface (@ifaces) {
-               if (!exists($KnownSymbols{$iface}) || $KnownSymbols{$iface} == 1) {
-                   push (@knownIfaces, $iface);
-               }
-             }
-
-           $Interfaces{$object} = join(' ', @knownIfaces);
-           @TRACE@("Interfaces for $object: $Interfaces{$object}\n");
-       } else {
-         @TRACE@("skipping interfaces for unknown symbol: $object\n");
-       }
-    }
-    close (INPUT);
-}
+def ReadInterfaces():
+    global Interfaces
+    Interfaces = {}
+
+    if not os.path.isfile(INTERFACES_FILE):
+        return
+
+    INPUT = open(INTERFACES_FILE)
+
+    for line in INPUT:
+        line = line.strip()
+        (gobject, ifaces) = line.split()
+        if gobject in KnownSymbols and KnownSymbols[gobject] == 1:
+            knownIfaces = []
+
+            # filter out private interfaces, but leave foreign interfaces
+            for iface in ifaces:
+                if iface not in KnownSymbols or KnownSymbols[iface] == 1:
+                    knownIfaces.append(iface)
+
+
+
+            Interfaces[gobject] = ' '.join(knownIfaces)
+            logging.info("Interfaces for %s: %s\n", gobject, Interfaces[gobject])
+        else:
+            logging.info("skipping interfaces for unknown symbol: %s", gobject)
+
+    INPUT.close()
+
 
 #############################################################################
 # Function    : ReadPrerequisites
@@ -6230,35 +5802,30 @@ sub ReadInterfaces {
 # Arguments   : none
 #############################################################################
 
-sub ReadPrerequisites {
-    %Prerequisites = ();
-
-    if (! -f $PREREQUISITES_FILE) {
-        return;
-    }
-    if (!open (INPUT, $PREREQUISITES_FILE)) {
-        warn "Can't open $PREREQUISITES_FILE - skipping prerequisites\n";
-        return;
-    }
-
-    while (<INPUT>) {
-       chomp;
-       my ($iface, @prereqs) = split;
-       if (exists($KnownSymbols{$iface}) && $KnownSymbols{$iface} == 1) {
-           my @knownPrereqs = ();
-
-           # filter out private prerequisites, but leave foreign prerequisites
-           foreach my $prereq (@prereqs) {
-               if (!exists($KnownSymbols{$prereq}) || $KnownSymbols{$prereq} == 1) {
-                  push (@knownPrereqs, $prereq);
-               }
-           }
-
-           $Prerequisites{$iface} = join(' ', @knownPrereqs);
-       }
-    }
-    close (INPUT);
-}
+def ReadPrerequisites():
+    global Prerequisites
+    Prerequisites = {}
+
+    if not os.path.isfile(PREREQUISITES_FILE):
+        return
+
+    INPUT = open(PREREQUISITES_FILE)
+
+    for line in INPUT:
+        line = line.strip()
+        (iface, prereqs) = line.split()
+        if iface in KnownSymbols and KnownSymbols[iface] == 1:
+            knownPrereqs = []
+
+            # filter out private prerequisites, but leave foreign prerequisites
+            for prereq in prereqs:
+                if prereq not in KnownSymbols or KnownSymbols[prereq] == 1:
+                    knownPrereqs.append(prereq)
+
+            Prerequisites[iface] = ' '.join(knownPrereqs)
+
+    INPUT.close()
+
 
 #############################################################################
 # Function    : ReadArgsFile
@@ -6269,91 +5836,95 @@ sub ReadPrerequisites {
 # Arguments   : $file - the file containing the arg information.
 #############################################################################
 
-sub ReadArgsFile {
-    my ($file) = @_;
-
-    my $in_arg = 0;
-    my $arg_object;
-    my $arg_name;
-    my $arg_type;
-    my $arg_flags;
-    my $arg_nick;
-    my $arg_blurb;
-    my $arg_default;
-    my $arg_range;
+def ReadArgsFile(ifile):
+    in_arg = False
+    arg_object = None
+    arg_name = None
+    arg_type = None
+    arg_flags = None
+    arg_nick = None
+    arg_blurb = None
+    arg_default = None
+    arg_range = None
 
     # Reset the args info.
-    @ArgObjects = ();
-    @ArgNames = ();
-    @ArgTypes = ();
-    @ArgFlags = ();
-    @ArgNicks = ();
-    @ArgBlurbs = ();
-    @ArgDefaults = ();
-    @ArgRanges = ();
-
-    if (! -f $file) {
-        return;
-    }
-    if (!open (INPUT, $file)) {
-        warn "Can't open $file - skipping args\n";
-        return;
-    }
-    while (<INPUT>) {
-        if (!$in_arg) {
-            if (m/^<ARG>/) {
-                $in_arg = 1;
-                $arg_object = "";
-                $arg_name = "";
-                $arg_type = "";
-                $arg_flags = "";
-                $arg_nick = "";
-                $arg_blurb = "";
-                $arg_default = "";
-                $arg_range = "";
-            }
-        } else {
-            if (m/^<NAME>(.*)<\/NAME>/) {
-                $arg_name = $1;
-                if ($arg_name =~ m/^(.*)::(.*)$/) {
-                    $arg_object = $1;
-                    ($arg_name = $2) =~ s/_/-/g;
-                    @TRACE@("Found arg: $arg_name\n");
-                } else {
-                    &LogWarning ($file, $., "Invalid argument name: $arg_name");
-                }
-            } elsif (m/^<TYPE>(.*)<\/TYPE>/) {
-                $arg_type = $1;
-            } elsif (m/^<RANGE>(.*)<\/RANGE>/) {
-                $arg_range = $1;
-            } elsif (m/^<FLAGS>(.*)<\/FLAGS>/) {
-                $arg_flags = $1;
-            } elsif (m/^<NICK>(.*)<\/NICK>/) {
-                $arg_nick = $1;
-            } elsif (m/^<BLURB>(.*)<\/BLURB>/) {
-                $arg_blurb = $1;
-                if ($arg_blurb eq "(null)") {
-                  $arg_blurb = "";
-                  &LogWarning ($file, $., "Property ${arg_object}:${arg_name} has no documentation.");
-                }
-            } elsif (m/^<DEFAULT>(.*)<\/DEFAULT>/) {
-                $arg_default = $1;
-            } elsif (m%^</ARG>%) {
-                @TRACE@("Found end of arg: ${arg_object}::${arg_name}\n${arg_type} : ${arg_flags}\n");
-                push (@ArgObjects, $arg_object);
-                push (@ArgNames, $arg_name);
-                push (@ArgTypes, $arg_type);
-                push (@ArgRanges, $arg_range);
-                push (@ArgFlags, $arg_flags);
-                push (@ArgNicks, $arg_nick);
-                push (@ArgBlurbs, $arg_blurb);
-                push (@ArgDefaults, $arg_default);
-                $in_arg = 0;
-            }
-        }
-    }
-    close (INPUT);
-}
+    global ArgObjects, ArgNames, ArgTypes, ArgFlags, ArgNicks, ArgBlurbs, ArgDefaults, ArgRanges
+    ArgObjects = []
+    ArgNames = []
+    ArgTypes = []
+    ArgFlags = []
+    ArgNicks = []
+    ArgBlurbs = []
+    ArgDefaults = []
+    ArgRanges = []
+
+    if not os.path.isfile(ifile):
+        return
+
+    INPUT = open(ifile)
+    line_number = 0
+    for line in INPUT:
+        line_number += 1
+        if not in_arg:
+            if re.search(r'^<ARG>', line):
+                in_arg = True
+                arg_object = ''
+                arg_name = ''
+                arg_type = ''
+                arg_flags = ''
+                arg_nick = ''
+                arg_blurb = ''
+                arg_default = ''
+                arg_range = ''
+
+        else:
+            m1 = re.search(r'^<NAME>(.*)<\/NAME>', line)
+            m2 = re.search(r'^<TYPE>(.*)<\/TYPE>', line)
+            m3 = re.search(r'^<RANGE>(.*)<\/RANGE>', line)
+            m4 = re.search(r'^<FLAGS>(.*)<\/FLAGS>', line)
+            m5 = re.search(r'^<NICK>(.*)<\/NICK>', line)
+            m6 = re.search(r'^<BLURB>(.*)<\/BLURB>', line)
+            m7 = re.search(r'^<DEFAULT>(.*)<\/DEFAULT>', line)
+            if m1:
+                arg_name = m1.group(1)
+                m1_1 = re.search(r'^(.*)::(.*)$', arg_name)
+                if m1_1:
+                    arg_object = m1_1.group(1)
+                    arg_name = m1_1.group(2).replace('_', '-')
+                    logging.info("Found arg: %s", arg_name)
+                else:
+                    common.LogWarning(ifile, line_number, "Invalid argument name: " + arg_name)
+
+            elif m2:
+                arg_type = m2.group(1)
+            elif m3:
+                arg_range = m3.group(1)
+            elif m4:
+                arg_flags = m4.group(1)
+            elif m5:
+                arg_nick = m5.group(1)
+            elif m6:
+                arg_blurb = m6.group(1)
+                if arg_blurb == "(null)":
+                    arg_blurb = ''
+                    common.LogWarning(ifile, line_number, "Property %s:%s has no documentation." % 
(arg_object, arg_name))
+
+            elif m7:
+                arg_default = m7.group(1)
+            elif re.search(r'^</ARG>', line):
+                logging.info("Found end of arg: %s::%s\n%s : %s\n", arg_object, arg_name, arg_type, 
arg_flags)
+                ArgObjects.append(arg_object)
+                ArgNames.append(arg_name)
+                ArgTypes.append(arg_type)
+                ArgRanges.append(arg_range)
+                ArgFlags.append(arg_flags)
+                ArgNicks.append(arg_nick)
+                ArgBlurbs.append(arg_blurb)
+                ArgDefaults.append(arg_default)
+                in_arg = False
+
+    INPUT.close()
+
 
 #############################################################################
 # Function    : AddTreeLineArt
@@ -6362,41 +5933,37 @@ sub ReadArgsFile {
 # Arguments   : @tree - array of indented strings.
 #############################################################################
 
-sub AddTreeLineArt {
-  my @tree = @{$_[0]};
-  my $i;
-  my $j;
-  my $indent;
-
-  # iterate bottom up over the tree
-  for ($i = $#tree; $i >= 0; $i--) {
-    # count leading spaces
-    $tree[$i] =~ /^([^<A-Za-z]*)/;
-    $indent = length( $1 );
-    # replace with ╰───, if place of ╰ is not space insert ├
-    if ($indent > 4) {
-      if (substr($tree[$i],$indent-4,1) eq " ") {
-        substr($tree[$i],$indent-4,4) = "--- ";
-      } else {
-        substr($tree[$i],$indent-4,4) = "+-- ";
-      }
-      # go lines up while space and insert |
-      for ($j = $i - 1; ($j >= 0 && substr($tree[$j],$indent-4,1) eq ' '); $j--) {
-        substr($tree[$j],$indent-4,1) = '|';
-      }
-    }
-  }
-
-  my $res = join("\n", @tree);
-  # unicode chars for: ╰──
-  $res =~ s%---%<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>%g;
-  # unicde chars for: ├──
-  $res =~ s%\+--%<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>%g;
-  # unicode char for: │
-  $res =~ s%\|%<phrase role=\"lineart\">&#9474;</phrase>%g;
-
-  return $res;
-}
+def AddTreeLineArt(tree):
+    # iterate bottom up over the tree
+    for i in range(len(tree) - 1, -1, -1):
+        # count leading spaces
+        m = re.search(r'^([^<A-Za-z]*)', tree[i])
+        tree[i] = m.group(0)
+        indent = len(m.group(1))
+        # replace with ╰───, if place of ╰ is not space insert ├
+        if indent > 4:
+            if tree[i][indent-4] == " ":
+                tree[i] = tree[i][:indent-4] + "--- " + tree[i][indent:]
+            else:
+                tree[i] = tree[i][:indent-4] + "+-- " + tree[i][indent:]
+
+            # go lines up while space and insert |
+            j = i - 1
+            while j >= 0 and tree[j][indent-4] == ' ':
+                tree[j][indent] = '|'
+                j -= 1
+
+
+    res = "\n".join(tree)
+    # unicode chars for: ╰──
+    res = re.sub(r'---', '<phrase role=\"lineart\">&#9584;&#9472;&#9472;</phrase>', res)
+    # unicde chars for: ├──
+    res = re.sub(r'\+--', '<phrase role=\"lineart\">&#9500;&#9472;&#9472;</phrase>', res)
+    # unicode char for: │
+    res = re.sub(r'\|', '<phrase role=\"lineart\">&#9474;</phrase>', res)
+
+    return res
+
 
 
 #############################################################################
@@ -6408,13 +5975,12 @@ sub AddTreeLineArt {
 # Arguments   : $name - the name to check.
 #############################################################################
 
-sub CheckIsObject {
-    my ($name) = @_;
-    my $root = $ObjectRoots{$name};
+def CheckIsObject(name):
+    root = ObjectRoots[name]
     # Let GBoxed pass as an object here to get -struct appended to the id
     # and prevent conflicts with sections.
-    return (defined($root) and $root ne 'GEnum' and $root ne 'GFlags');
-}
+    return root and root != 'GEnum' and root != 'GFlags'
+
 
 
 #############################################################################
@@ -6423,11 +5989,10 @@ sub CheckIsObject {
 # Arguments   : $str - the string to pad.
 #############################################################################
 
-sub MakeReturnField {
-    my ($str) = @_;
+def MakeReturnField(s):
+
+    return str + (' ' * (RETURN_TYPE_FIELD_WIDTH - len(s)))
 
-    return $str . (' ' x ($RETURN_TYPE_FIELD_WIDTH - length ($str)));
-}
 
 #############################################################################
 # Function    : GetSymbolSourceFile
@@ -6435,17 +6000,12 @@ sub MakeReturnField {
 # Arguments   : $symbol - the symbol name
 #############################################################################
 
-sub GetSymbolSourceFile {
-    my ($symbol) = @_;
-
-    if (defined($SourceSymbolSourceFile{$symbol})) {
-        return $SourceSymbolSourceFile{$symbol};
-    } elsif (defined($SymbolSourceFile{$symbol})) {
-        return $SymbolSourceFile{$symbol};
-    } else {
-        return "";
-    }
-}
+def GetSymbolSourceFile(symbol):
+    if symbol in SourceSymbolSourceFile:
+        return SourceSymbolSourceFile[symbol]
+    if symbol in SymbolSourceFile:
+        return SymbolSourceFile[symbol]
+    return ''
 
 #############################################################################
 # Function    : GetSymbolSourceLine
@@ -6453,15 +6013,13 @@ sub GetSymbolSourceFile {
 # Arguments   : $symbol - the symbol name
 #############################################################################
 
-sub GetSymbolSourceLine {
-    my ($symbol) = @_;
+def GetSymbolSourceLine(symbol):
+    if symbol in SourceSymbolSourceLine:
+        return SourceSymbolSourceLine[symbol]
+    if symbol in SymbolSourceLine[symbol]:
+        return SymbolSourceLine[symbol]
+    return 0
 
-    if (defined($SourceSymbolSourceLine{$symbol})) {
-        return $SourceSymbolSourceLine{$symbol};
-    } elsif (defined($SymbolSourceLine{$symbol})) {
-        return $SymbolSourceLine{$symbol};
-    } else {
-        return 0;
-    }
-}
 
+if __name__ == '__main__':
+    Run()
diff --git a/tests/Makefile.am b/tests/Makefile.am
index fea2c88..44e1a54 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,7 +5,7 @@ SUBDIRS = gobject bugs annotations fail empty program .
 if BUILD_TESTS
 
 TESTS = \
-  gtkdoc-common.t gtkdoc-mkdb.t \
+  gtkdoc-common.t \
   gtkdoc-check.py gtkdoc-common.py \
   tools.sh gobject.sh bugs.sh annotations.sh fail.sh empty.sh sanity.sh \
   program.sh
diff --git a/tests/tools.sh.in b/tests/tools.sh.in
index 309de0b..ac930e4 100644
--- a/tests/tools.sh.in
+++ b/tests/tools.sh.in
@@ -10,7 +10,7 @@ echo "Running suite(s): gtk-doc-$suite";
 # we can use which here as we override the path in TEST_ENVIRONMENT
 
 # test perl scripts
-for file in gtkdoc-check gtkdoc-fixxref gtkdoc-mkdb gtkdoc-scangobj ; do
+for file in gtkdoc-check gtkdoc-fixxref gtkdoc-scangobj ; do
   @PERL@ -cwT `which $file`
   if test $? = 1 ; then failed=`expr $failed + 1`; fi
   tested=`expr $tested + 1`


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]