[dia/zbrown/python-3: 1/2] python: port to python3, pygobject




commit 75ca7c555871f12549a03e9a2933f68c31a2c2a0
Author: Zander Brown <zbrown gnome org>
Date:   Thu Oct 15 23:44:45 2020 +0100

    python: port to python3, pygobject
    
    Run 2to3 over existing scripts with a few manual tweaks, probably a few
    problems in there but all the extensions seem to run okay from light
    testing
    
    Actually introduced a warning as pygobject doesn't like using gtk2, but
    pygtk isn't available for python3

 bindings/test.py                    |  22 +-
 lib/diatypes.h                      |   1 -
 lib/geometry.c                      |   8 -
 lib/geometry.h                      |  20 -
 lib/libdia.def                      |   2 -
 lib/sheet.c                         |   8 +-
 objects/custom/read_path.py         |  20 +-
 plug-ins/cgm/cgm.py                 |   2 +-
 plug-ins/python/allprops.py         |  27 +-
 plug-ins/python/allsheets.py        |  26 +-
 plug-ins/python/aobjects.py         |  12 +-
 plug-ins/python/arrange.py          |  22 +-
 plug-ins/python/autolayoutforce.py  |   4 +-
 plug-ins/python/codegen.py          |  12 +-
 plug-ins/python/debug_objects.py    |  14 +-
 plug-ins/python/dia_rotate.py       |  40 +-
 plug-ins/python/diagx.py            |  82 ++--
 plug-ins/python/diamodule.c         | 104 +++--
 plug-ins/python/diastddia.py        |   2 +-
 plug-ins/python/diasvg.py           |  12 +-
 plug-ins/python/diasvg_import.py    | 112 ++---
 plug-ins/python/dot.py              |  10 +-
 plug-ins/python/dot2dia.py          |  66 +--
 plug-ins/python/doxrev.py           |  74 ++--
 plug-ins/python/export-object.py    |   2 +-
 plug-ins/python/group_props.py      | 183 --------
 plug-ins/python/gtkcons.py          | 654 +++++++++++++++-------------
 plug-ins/python/imgmap.py           |  12 +-
 plug-ins/python/meson.build         |  11 +-
 plug-ins/python/otypes.py           |  28 +-
 plug-ins/python/pydia-color.c       | 118 +++--
 plug-ins/python/pydia-cpoint.c      | 141 +++---
 plug-ins/python/pydia-diagram.c     |   8 +-
 plug-ins/python/pydia-diagramdata.c |  36 +-
 plug-ins/python/pydia-display.c     | 122 +++---
 plug-ins/python/pydia-error.c       | 128 +++---
 plug-ins/python/pydia-export.c      | 101 +++--
 plug-ins/python/pydia-font.c        | 185 ++++----
 plug-ins/python/pydia-geometry.c    | 695 ++++++++++++++++--------------
 plug-ins/python/pydia-geometry.h    |  13 +-
 plug-ins/python/pydia-handle.c      | 141 +++---
 plug-ins/python/pydia-image.c       | 138 +++---
 plug-ins/python/pydia-layer.c       |  40 +-
 plug-ins/python/pydia-menuitem.c    | 118 ++---
 plug-ins/python/pydia-object.c      | 279 ++++++------
 plug-ins/python/pydia-paperinfo.c   | 151 ++++---
 plug-ins/python/pydia-properties.c  | 236 +++++-----
 plug-ins/python/pydia-properties.h  |   4 +-
 plug-ins/python/pydia-property.c    | 831 ++++++++++++++++++++++--------------
 plug-ins/python/pydia-render.c      |  10 +-
 plug-ins/python/pydia-sheet.c       | 157 +++----
 plug-ins/python/pydia-text.c        | 164 ++++---
 plug-ins/python/pydia.h             |  33 ++
 plug-ins/python/pydiadoc.py         |  24 +-
 plug-ins/python/python-startup.py   |   2 +-
 plug-ins/python/python.c            |  19 +-
 plug-ins/python/scascale.py         |  52 +--
 plug-ins/python/select_by.py        |  40 +-
 plug-ins/python/uninline_data.py    |   8 +-
 plug-ins/python/wdeps.py            | 472 ++++++++++----------
 samples/gobj-parse.py               | 119 +++---
 61 files changed, 3256 insertions(+), 2921 deletions(-)
---
diff --git a/bindings/test.py b/bindings/test.py
index 67a613ed1..66f10c230 100644
--- a/bindings/test.py
+++ b/bindings/test.py
@@ -4,7 +4,7 @@ import os, sys
 format_extensions = ["drs", "svg", "png"]
 
 if sys.platform == "win32" :
-       print "Adjusting PATH ..."
+       print("Adjusting PATH ...")
        sys.path.insert(0, r'd:\graph\dia2\python')
        sys.path.insert(0, r'd:\graph\dia2\bin')
        sys.path.insert(0, r'..\plug-ins\python')
@@ -17,19 +17,19 @@ import dia
 
 
 def Dump () :
-       print dir(dia)
+       print(dir(dia))
 
        r = dia.Rectangle
-       print r, dir(r)
+       print(r, dir(r))
        o = dia.Object
-       print o, dir(o)
+       print(o, dir(o))
        rd = dia.Renderer
-       print rd, dir(rd)
+       print(rd, dir(rd))
 
 if sys.platform == "win32" :
        os.environ["DIA_LIB_PATH"] = r"d:\graph\dia2\dia"
 else :
-       print "FIXME: trouble with dynamic loading on '%s'?" % (sys.platform,)
+       print("FIXME: trouble with dynamic loading on '%s'?" % (sys.platform,))
        base_path = os.getcwd() + "/.."
        os.environ["DIA_LIB_PATH"] = base_path + "/objects//:" + base_path + "/plug-ins//"
        os.environ["DIA_SHAPE_PATH"] = base_path + "/shapes//"
@@ -38,7 +38,7 @@ else :
 try :
        dia.register_plugins()
 except AttributeError :
-       print "Wrong '%s' picked up?" % (dia.__file__, )
+       print("Wrong '%s' picked up?" % (dia.__file__, ))
 
 def Export (name, data) :
        # write data to file, the format is choosen here
@@ -56,11 +56,11 @@ def Import () :
        filename = "render-test-swig.svg"
        ef = dia.filter_export_get_by_name ("svg")
        if not ef :
-               print "Guessing ..."
+               print("Guessing ...")
                ef = dia.filter_guess_export_filter ("dummy.png")
                filename = "render-test-swig.png"
 
-       print ef, "\n", ef.description, "\n", dir(ef)
+       print(ef, "\n", ef.description, "\n", dir(ef))
 
        ef.do_export (data, filename)
 
@@ -135,8 +135,8 @@ def Self () :
        r = data.extents
        try :
                data.extents = r
-       except AttributeError, s :
-               print "Expected except", s, r.top, r.left
+       except AttributeError as s :
+               print("Expected except", s, r.top, r.left)
 
 for arg in sys.argv[1:] :
        if '--dump' == arg : Dump ()
diff --git a/lib/diatypes.h b/lib/diatypes.h
index 7fa332d06..c090de15f 100644
--- a/lib/diatypes.h
+++ b/lib/diatypes.h
@@ -105,7 +105,6 @@ typedef struct _DiaFontClass DiaFontClass;
 /* In geometry.h: */
 typedef struct _Point Point;
 typedef struct _DiaRectangle DiaRectangle;
-typedef struct _IntRectangle IntRectangle;
 typedef struct _BezPoint BezPoint;
 typedef struct _DiaMatrix DiaMatrix;
 
diff --git a/lib/geometry.c b/lib/geometry.c
index e2246e7ce..c1ed17453 100644
--- a/lib/geometry.c
+++ b/lib/geometry.c
@@ -38,14 +38,6 @@ rectangle_union (DiaRectangle *r1, const DiaRectangle *r2)
   r1->right = MAX( r1->right, r2->right );
 }
 
-void
-int_rectangle_union (IntRectangle *r1, const IntRectangle *r2)
-{
-  r1->top = MIN( r1->top, r2->top );
-  r1->bottom = MAX( r1->bottom, r2->bottom );
-  r1->left = MIN( r1->left, r2->left );
-  r1->right = MAX( r1->right, r2->right );
-}
 
 void
 rectangle_intersection (DiaRectangle *r1, const DiaRectangle *r2)
diff --git a/lib/geometry.h b/lib/geometry.h
index 05e964c4f..11f78cf7a 100644
--- a/lib/geometry.h
+++ b/lib/geometry.h
@@ -115,25 +115,6 @@ struct _DiaRectangle {
 };
 
 
-/**
- * IntRectangle:
- * @left: top left x co-ord
- * @top: top left y co-ord
- * @right: bottom right x co-ord
- * @button: bottom right y co-ord
- *
- * A rectangle for fixed point e.g. pixel doubleinates
- *
- * Since: dawn-of-time
- */
-struct _IntRectangle {
-  int left;
-  int top;
-  int right;
-  int bottom;
-};
-
-
 /**
  * BezPoint:
  * @BEZ_MOVE_TO: move to point @p1
@@ -312,7 +293,6 @@ point_copy_add_scaled(Point *dst, const Point *src,
 void point_convex(Point *dst, const Point *src1, const Point *src2, real alpha);
 
 void rectangle_union(DiaRectangle *r1, const DiaRectangle *r2);
-void int_rectangle_union(IntRectangle *r1, const IntRectangle *r2);
 void rectangle_intersection(DiaRectangle *r1, const DiaRectangle *r2);
 int rectangle_intersects(const DiaRectangle *r1, const DiaRectangle *r2);
 int point_in_rectangle(const DiaRectangle* r, const Point *p);
diff --git a/lib/libdia.def b/lib/libdia.def
index 689da22b2..9c6793cbe 100644
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -569,8 +569,6 @@ EXPORTS
  get_active_focus
  give_focus
 
- int_rectangle_union
-
  intl_score_locale
 
  dia_layer_add_object
diff --git a/lib/sheet.c b/lib/sheet.c
index f70a26d02..f9cc93e02 100644
--- a/lib/sheet.c
+++ b/lib/sheet.c
@@ -108,7 +108,12 @@ static void load_register_sheet  (const char *directory,
                                   SheetScope  scope);
 
 
-/** Sort the list of sheets by *locale*.
+/**
+ * dia_sheet_sort_callback:
+ * @a: #Sheet A
+ * @b: #Sheet B
+ *
+ * Sort the list of sheets by *locale*.
  */
 static int
 dia_sheet_sort_callback (gconstpointer a, gconstpointer b)
@@ -118,6 +123,7 @@ dia_sheet_sort_callback (gconstpointer a, gconstpointer b)
                          gettext (((Sheet *) (b))->name));
 }
 
+
 void
 dia_sort_sheets (void)
 {
diff --git a/objects/custom/read_path.py b/objects/custom/read_path.py
index d9d34d6ad..63d374a6b 100644
--- a/objects/custom/read_path.py
+++ b/objects/custom/read_path.py
@@ -1,6 +1,4 @@
 
-import string
-
 MOVE_TO = 0
 LINE_TO = 1
 CURVE_TO = 2
@@ -18,13 +16,13 @@ class Component:
                self.point3 = point3
        def dump(self):
                if self.type == MOVE_TO:
-                       print "Move To", self.point1
+                       print("Move To", self.point1)
                elif self.type == LINE_TO:
-                       print "Line To", self.point1
+                       print("Line To", self.point1)
                elif self.type == CURVE_TO:
-                       print "Curve To", self.point1, self.point2, self.point3
+                       print("Curve To", self.point1, self.point2, self.point3)
                elif self.type == CLOSE:
-                       print "Close"
+                       print("Close")
 
 def chomp(bpath):
        while bpath and bpath[0] in ' \t\n\r,':
@@ -36,7 +34,7 @@ def read_num(str):
        while str and str[0] in '0123456789.+-':
                num = num + str[0]
                str = str[1:]
-       return string.atof(num), chomp(str)
+       return float(num), chomp(str)
 
 def parse_bpath(bpath):
        p = []
@@ -46,11 +44,11 @@ def parse_bpath(bpath):
        last_control = (0,0)
        last_type = MOVE_TO
        last_relative = 0
-       
+
        bpath = chomp(bpath)
        while bpath:
                # grok new commands
-               print bpath
+               print(bpath)
                if bpath[0] == 'M':
                        bpath = chomp(bpath[1:])
                        last_type = MOVE_TO
@@ -106,7 +104,7 @@ def parse_bpath(bpath):
                        last_type = CLOSE
                        last_relative = 0
                elif bpath[0] not in '0123456789.+-':
-                       raise TypeError, "unexpected input"
+                       raise TypeError("unexpected input")
 
                if last_type == MOVE_TO:
                        x, bpath = read_num(bpath)
@@ -184,7 +182,7 @@ def parse_bpath(bpath):
 
 def print_bpath(bpath):
        p = parse_bpath(bpath)
-       map(Component.dump, p)
+       list(map(Component.dump, p))
 
 if __name__ == '__main__':
        import sys
diff --git a/plug-ins/cgm/cgm.py b/plug-ins/cgm/cgm.py
index e68d38cf2..b32f3ebce 100644
--- a/plug-ins/cgm/cgm.py
+++ b/plug-ins/cgm/cgm.py
@@ -13,7 +13,7 @@ def read_cgm(file):
                        head = file.read(2)
                        if len(head) != 2: break
                        el_len = (ord(head[0]) << 8) | ord(head[1])
-               print (el_class, el_id, el_len)
+               print((el_class, el_id, el_len))
 
                el_body = file.read(el_len)
                if el_len & 1 == 1:  # odd element length
diff --git a/plug-ins/python/allprops.py b/plug-ins/python/allprops.py
index 2c00d344b..a832baf7c 100644
--- a/plug-ins/python/allprops.py
+++ b/plug-ins/python/allprops.py
@@ -19,7 +19,8 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import sys, dia, string
+import sys, dia
+from functools import cmp_to_key
 
 import gettext
 _ = gettext.gettext
@@ -42,19 +43,19 @@ def allprops_cb(data, flags) :
                try :
                        obj, h1, h2 = dia.get_object_type(oname).create (0, 0)
                except :
-                       print "Huh?", oname
+                       print("Huh?", oname)
                        continue
-               prop_keys = obj.properties.keys()
+               prop_keys = list(obj.properties.keys())
                for k in prop_keys :
                        p = obj.properties[k]
-                       if props_by_name.has_key(k) :
+                       if k in props_by_name :
                                # check if it is the same type
                                p0, names = props_by_name[k]
                                try :
                                        if p0.type != p.type :
                                                # construct a unique name
                                                uname = p.name + "<" + p.type + ">"
-                                               if props_by_name.has_key(uname) :
+                                               if uname in props_by_name :
                                                        props_by_name[uname][1].append(oname)
                                                else :
                                                        props_by_name[uname] = (p, [oname])
@@ -63,7 +64,7 @@ def allprops_cb(data, flags) :
                                                # remember the origin of the property
                                                props_by_name[k][1].append(oname)
                                except KeyError :
-                                       print oname, "::", k, p, "?"
+                                       print(oname, "::", k, p, "?")
                        else :
                                props_by_name[k] = (p, [oname])
                obj.destroy() # unsave delete, any method call will fail/crash afterweards
@@ -74,10 +75,10 @@ def allprops_cb(data, flags) :
        dy = 5.0
        ot = dia.get_object_type("UML - Class")
 
-       props_keys = props_by_name.keys()
+       props_keys = list(props_by_name.keys())
        # alpha-numeric sorting by type; after by number of users
-       props_keys.sort (lambda a,b : len(props_by_name[b][1]) - len(props_by_name[a][1]))
-       props_keys.sort (lambda a,b : cmp(props_by_name[a][0].type, props_by_name[b][0].type))
+       props_keys.sort (key=cmp_to_key (lambda a,b : len(props_by_name[b][1]) - len(props_by_name[a][1])))
+       props_keys.sort (key=lambda a: props_by_name[a][0].type)
 
        almost_all = 98 * len(otypes) / 100 # 98 %
 
@@ -87,11 +88,11 @@ def allprops_cb(data, flags) :
 
                x = 0.0
                y = 0.0
-               if grid.has_key(p.type) :
+               if p.type in grid:
                        x, y = grid[p.type]
                else :
                        x = 0.0
-                       y = len(grid.keys()) * dy
+                       y = len(list(grid.keys())) * dy
                o, h1, h2 = ot.create (x,y)
                o.properties["name"] = pname
                o.properties["template"] = 1
@@ -111,7 +112,7 @@ def allprops_cb(data, flags) :
                        o.properties["visible_comments"] = 1
                        o.properties["comment_line_length"] = 60
                else :
-                       o.properties["comment"] = string.join(names, "; ")
+                       o.properties["comment"] = "; ".join(names)
                        o.properties["visible_comments"] = 0
                        o.properties["comment_line_length"] = 60
 
@@ -126,7 +127,7 @@ def allprops_cb(data, flags) :
                diagram.display()
                diagram.flush()
        if len(name_type_clashes) > 0 :
-               dia.message(0, "One name, one type?!\n" + string.join(name_type_clashes, "\n"))
+               dia.message(0, "One name, one type?!\n" + "\n".join(name_type_clashes))
        return data
 
 dia.register_action ("HelpAllPropts", _("All Object _Properties"),
diff --git a/plug-ins/python/allsheets.py b/plug-ins/python/allsheets.py
index ba54ea77e..3f4806120 100644
--- a/plug-ins/python/allsheets.py
+++ b/plug-ins/python/allsheets.py
@@ -17,7 +17,7 @@
 #   You should have received a copy of the GNU General Public License
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-import dia, os, string
+import dia, os
 import tempfile
 import webbrowser
 
@@ -31,13 +31,13 @@ def so_get_namespace (sol) :
                return "Empty"
        for ot, descr, fname in sol :
                if ot :
-                       sp = string.split(ot.name, " - ")
+                       sp = ot.name.split(" - ")
                        if len(sp) > 1 :
-                               if names.has_key (sp[0]) :
+                               if sp[0] in names:
                                        names[sp[0]] += 1
                                else :
                                        names[sp[0]] = 1
-       return string.join (names.keys(), ",")
+       return ",".join (list(names.keys()))
 
 def check_objecttype_overlap (sheets) :
        types = dia.registered_types()
@@ -48,18 +48,18 @@ def check_objecttype_overlap (sheets) :
                del types["Standard - %s" % (s,)]
        # got through all the sheets to match against registered types
        missing = []
-       for sheet in sheets :
-               for ot, descr, fname in sheet.objects :
-                       if types.has_key (ot.name) :
-                               if ot == types[ot.name] :
+       for sheet in sheets:
+               for ot, descr, fname in sheet.objects:
+                       if ot.name in types:
+                               if ot == types[ot.name]:
                                        del types[ot.name]
-                               else :
-                                       print "Mix-up:", ot.name
-                       else :
+                               else:
+                                       print("Mix-up:", ot.name)
+                       else:
                                # sheet referencing a type not available
-                               missing.append (ot.name)
+                               missing.append(ot.name)
        # from the dictionary removed every type referenced just once?
-       print types
+       print(types)
 
 def isheets_cb (data, flags) :
        sheets = dia.registered_sheets ()
diff --git a/plug-ins/python/aobjects.py b/plug-ins/python/aobjects.py
index 9be5a388d..9f125a8fb 100644
--- a/plug-ins/python/aobjects.py
+++ b/plug-ins/python/aobjects.py
@@ -19,13 +19,13 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import sys, dia, string
+import sys, dia
 
 import gettext
 _ = gettext.gettext
 
 def set_object_string (o) :
-       keys = o.properties.keys()
+       keys = list(o.properties.keys())
        for s in keys :
                p = o.properties[s]
                if p.type in ["string", "text"] :
@@ -45,12 +45,12 @@ def aobjects_cb(data, flags) :
        layer = data.active_layer
 
        otypes = dia.registered_types()
-       keys = otypes.keys()
+       keys = list(otypes.keys())
        keys.sort()
 
        packages = {}
        for s in keys :
-               kt = string.split(s, " - ")
+               kt = " - ".split(s)
                if len(kt) == 2 :
                        if len(kt[0]) == 0 :
                                sp = "<unnamed>"
@@ -60,12 +60,12 @@ def aobjects_cb(data, flags) :
                else :
                        sp = "<broken>"
                        st = kt[0]
-               if packages.has_key(sp) :
+               if sp in packages:
                        packages[sp].append(s)
                else :
                        packages[sp] = [s]
 
-       for sp in packages.keys() :
+       for sp in list(packages.keys()):
                # add a layer per package
                layer = data.add_layer (sp)
 
diff --git a/plug-ins/python/arrange.py b/plug-ins/python/arrange.py
index 7d8303410..b22814980 100644
--- a/plug-ins/python/arrange.py
+++ b/plug-ins/python/arrange.py
@@ -25,11 +25,11 @@ def DeepCalc (dict, key, seen = None) :
        if not seen :
                seen = {}
        for k in dict[key][2] :
-               if seen and seen.has_key (k) :
+               if seen and k in seen:
                        continue
                seen[k] = 1
                DeepCalc (dict, k, seen)
-       return len(seen.keys())
+       return len(list(seen.keys()))
 
 ##
 # \brief Callback function to be invoked by Dia's menu
@@ -42,7 +42,7 @@ def arrange_connected (data, flags) :
                objs = data.active_layer.objects
        # all objects having at least one connection
        bs = {}
-       print "objs", len(objs)
+       print("objs", len(objs))
        edges = {}
        for o in objs :
                for c in o.connections: # ConnectionPoint
@@ -55,17 +55,17 @@ def arrange_connected (data, flags) :
                                bk = b.properties["name"].value
                                # create an edge key
                                ek = ak + "->" + bk
-                               if edges.has_key (ek) :
+                               if ek in edges:
                                        continue # already seen
-                               print ek
+                               print(ek)
                                edges[ek] = 1
-                               if bs.has_key (ak) :
+                               if ak in bs:
                                        use = bs[ak]
                                        use[2].append (bk)
                                        bs[ak] = use
                                else :
                                        bs[ak] = [a, 0, [bk]]
-                               if bs.has_key (bk) :
+                               if bk in bs:
                                        use = bs[bk]
                                        use[1] += 1
                                        bs[bk] = use
@@ -75,16 +75,16 @@ def arrange_connected (data, flags) :
        bst = []
        dx = 0
        dy = 0
-       for key in bs.keys() :
+       for key in list(bs.keys()) :
                o = bs[key][0]
                if not o.properties["elem_width"] :
-                       print o
+                       print(o)
                        continue
                if o.properties["elem_width"].value > dx : dx = o.properties["elem_width"].value
                if o.properties["elem_height"].value > dy : dy = o.properties["elem_height"].value
                n = bs[key][1] # (use count, dependencies)
                bst.append ((o, n, DeepCalc (bs, key)))
-       bst.sort (lambda a, b : cmp(a[1], b[1]))
+       bst.sort(key=lambda a: a[1])
        if len(bs) < 2 :
                return
        # average weight gives the number of rows
@@ -109,7 +109,7 @@ def arrange_connected (data, flags) :
                        elif t[1] >= rows :
                                # compensate for some of the weight, FIXME: better guess needed
                                y = int(rows - 2 * aw / (t[2] + t[1]))
-               print t[0].properties["name"].value, t[1], t[2], y, c
+               print(t[0].properties["name"].value, t[1], t[2], y, c)
                # move the object to it's new place
                x = offsets[y]
                t[0].move (x * dx, y * dy)
diff --git a/plug-ins/python/autolayoutforce.py b/plug-ins/python/autolayoutforce.py
index 8b452e628..3db09aa72 100644
--- a/plug-ins/python/autolayoutforce.py
+++ b/plug-ins/python/autolayoutforce.py
@@ -55,7 +55,7 @@ def repulsion (rconst, node, other) :
                fx = (numer * dx) / denom
                fy = (numer * dy) / denom
        except ZeroDivisionError :
-               print "ZeroDivisionError"
+               print("ZeroDivisionError")
                return (0,0)
 
        return (fx,fy)
@@ -120,7 +120,7 @@ def layout_force_cb(data, flags):
        data.active_layer.update_extents() # data/diagram _update_extents don't recalculate?
        data.update_extents ()
        data.flush()
-       print n, "iterations"
+       print(n, "iterations")
 
 dia.register_action ("LayoutForcePy", _("_Layout (force)"),
                      "/DisplayMenu/Test/TestExtensionStart",
diff --git a/plug-ins/python/codegen.py b/plug-ins/python/codegen.py
index 86ebe414b..ddf8b05a0 100644
--- a/plug-ins/python/codegen.py
+++ b/plug-ins/python/codegen.py
@@ -129,7 +129,7 @@ class PyRenderer(ObjRenderer) :
                ObjRenderer.__init__(self)
        def end_render(self) :
                f = open(self.filename, "w")
-               for sk in self.klasses.keys() :
+               for sk in list(self.klasses.keys()) :
                        parents = self.klasses[sk].parents + self.klasses[sk].templates
                        if not parents:
                                f.write ("class %s :\n" % (sk,))
@@ -165,7 +165,7 @@ class CxxRenderer(ObjRenderer) :
                f = open(self.filename, "w")
                f.write("/* generated by dia/codegen.py */\n")
                # declaration
-               for sk in self.klasses.keys() :
+               for sk in list(self.klasses.keys()) :
                        k = self.klasses[sk]
                        if len(k.comment) > 0 :
                                f.write ("/*" + k.comment + "*/\n")
@@ -238,7 +238,7 @@ class JsRenderer(ObjRenderer) :
        def end_render(self) :
                f = open(self.filename, "w")
                f.write("/* generated by dia/codegen.py */\n\n")
-               for sk in self.klasses.keys() :
+               for sk in list(self.klasses.keys()) :
                        f.write("// class definition : %s\n" % (sk, ))
                        parents = self.klasses[sk].parents
                        #add inheritance
@@ -356,7 +356,7 @@ class PascalRenderer(ObjRenderer) :
                f.write("/* generated by dia/codegen.py */\n")
                f.write("Type\n")
                # classes
-               class_names = self.klasses.keys()
+               class_names = list(self.klasses.keys())
                class_names.sort()
                # forward declarations of all classes
                for sk in class_names :
@@ -497,7 +497,7 @@ class JavaRenderer(ObjRenderer) :
 
                mainfile = open(self.filename, "w")
                mainfile.write("/* Generated by dia/codegen.py\n *\n * Generated files:\n")
-               for name, klass in self.klasses.iteritems() :
+               for name, klass in self.klasses.items() :
                        # splits the classes in separate files
                        classfile = self.filename[:self.filename.rfind("/")+1] + name.capitalize() + ".java"
                        f = open(classfile, "w")
@@ -611,7 +611,7 @@ class PhpRenderer(ObjRenderer) :
                mainfile = open(self.filename, "w")
                mainfile.write("<?php\n/* Generated by dia/codegen.py\n *\n * Generated files:\n")
 
-               for name, klass in self.klasses.iteritems() :
+               for name, klass in self.klasses.items() :
                        # splits the classes in separate files
                        classfile = self.filename[:self.filename.rfind("/")+1] + name + ".php"
                        f = open(classfile, "w")
diff --git a/plug-ins/python/debug_objects.py b/plug-ins/python/debug_objects.py
index 2948effed..8573a70df 100644
--- a/plug-ins/python/debug_objects.py
+++ b/plug-ins/python/debug_objects.py
@@ -8,23 +8,23 @@ _ = gettext.gettext
 def dia_debug_cb (data, flags) :
        "gets passed in the active diagram, flags are unused at the moment"
        for layer in data.layers :
-               print "Layer :", layer.name
+               print("Layer :", layer.name)
                for o in layer.objects :
-                       print str(o), str(o.bounding_box)
+                       print(str(o), str(o.bounding_box))
 
 def dia_debug_props_cb (data, flags) :
        for layer in data.layers :
-               print "Layer :", layer.name
+               print("Layer :", layer.name)
                for o in layer.objects :
-                       print str(o)
+                       print(str(o))
                        props = o.properties
-                       for s in props.keys() :
-                               print props[s].type + " " + s + " (visible=%d)" % props[s].visible
+                       for s in list(props.keys()) :
+                               print(props[s].type + " " + s + " (visible=%d)" % props[s].visible)
                                try :
                                        p = props[s].value
                                except :
                                        p = None
-                               print "\t" + str(p)
+                               print("\t" + str(p))
 
 # dia-python keeps a reference to the renderer class and uses it on demand
 dia.register_action ("DebugBoundingbox", _("Dia _BoundingBox Debugger"),
diff --git a/plug-ins/python/dia_rotate.py b/plug-ins/python/dia_rotate.py
index 619d7b73d..6f2388435 100644
--- a/plug-ins/python/dia_rotate.py
+++ b/plug-ins/python/dia_rotate.py
@@ -16,55 +16,57 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import dia, math, string
+import dia, math
 
 import gettext
 _ = gettext.gettext
 
 class CRotateDialog :
        def __init__(self, data) :
-               import pygtk
-               pygtk.require("2.0")
-               import gtk
-               win = gtk.Window()
+               import gi
+
+               gi.require_version('Gtk', '2.0')
+
+               from gi.repository import Gtk
+               win = Gtk.Window()
                win.connect("delete_event", self.on_delete)
                win.set_title(_("Rotate counter-clockwise"))
 
                self.diagram = data
                self.win = win
 
-               box1 = gtk.VBox()
+               box1 = Gtk.VBox()
                win.add(box1)
                box1.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2)
+               box1.pack_start(box2, True, True, 0)
                box2.show()
 
-               label1 = gtk.Label()
+               label1 = Gtk.Label()
                label1.set_text(_('Rotation around (0,0). Rotation angle in degrees:'))
-               box2.pack_start(label1)
+               box2.pack_start(label1, True, True, 0)
                label1.show()
 
-               self.entry = gtk.Entry()
+               self.entry = Gtk.Entry()
                self.entry.set_text("0.0")
-               box2.pack_start(self.entry)
+               box2.pack_start(self.entry, True, True, 0)
                self.entry.show()
 
-               separator = gtk.HSeparator()
-               box1.pack_start(separator, expand=0)
+               separator = Gtk.HSeparator()
+               box1.pack_start(separator, 0, True, 0)
                separator.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2, expand=0)
+               box1.pack_start(box2, 0, True, 0)
                box2.show()
 
-               button = gtk.Button("rotate")
+               button = Gtk.Button("rotate")
                button.connect("clicked", self.on_rotate)
-               box2.pack_start(button)
-               button.set_flags(gtk.CAN_DEFAULT)
+               box2.pack_start(button, True, True, 0)
+               button.set_can_default(True)
                button.grab_default()
                button.show()
                win.show()
diff --git a/plug-ins/python/diagx.py b/plug-ins/python/diagx.py
index 93e4ccffc..7e25ec831 100644
--- a/plug-ins/python/diagx.py
+++ b/plug-ins/python/diagx.py
@@ -18,7 +18,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import string, sys
+import sys
 
 import gettext
 _ = gettext.gettext
@@ -58,7 +58,7 @@ class Union(Node) :
                return self.name
                for s in self.names :
                        ms.append (g_nodes[s].Name())
-               return string.join(ms, "; ")
+               return "; ".join(ms)
 
 class Fassade(Node) :
        def __init__ (self, type, pre, post) :
@@ -78,7 +78,7 @@ class Argument(Node) :
                self.access = "public"
                self.static = 0
        def Type (self) :
-               if g_nodes.has_key (self.type) :
+               if self.type in g_nodes :
                        return g_nodes[self.type].Name()
                return "?"
        def Visibility (self) :
@@ -95,7 +95,7 @@ class Function(Node) :
        def AddArg (self, arg) :
                self.params.append (arg)
        def Type (self) :
-               if g_nodes.has_key (self.returns) :
+               if self.returns in g_nodes :
                        return g_nodes[self.returns].Name()
                return ""
        def Signature (self) :
@@ -107,10 +107,10 @@ class Function(Node) :
                        except AttributeError :
                                args.append (":" + p.Name())
                        except :
-                               print "E:", p, p.name, p.type
+                               print("E:", p, p.name, p.type)
                if self.returns :
                        ret = g_nodes[self.returns].Name() + " "
-               return ret + self.name + " (" + string.join(args, ", ") + ")"
+               return ret + self.name + " (" + ", ".join(args) + ")"
 
 class Method(Function) :
        def __init__ (self, name, type) :
@@ -144,7 +144,7 @@ class Klass(Node) :
        def IsClass (self) :
                return 1
        def Name (self) :
-               if g_nodes.has_key (self.context) and g_nodes[self.context].Name() != "" :
+               if self.context in g_nodes and g_nodes[self.context].Name() != "" :
                        return g_nodes[self.context].Name() + "::" + self.name
                else :
                        return self.name
@@ -152,26 +152,26 @@ class Klass(Node) :
                # full qualified names
                names = []
                for p in self.parents :
-                       if g_nodes.has_key(p) :
+                       if p in g_nodes :
                                names.append (g_nodes[p].Name())
                return names
        def Dump (self) :
                ps = ""
                for id in self.parents :
-                       if g_nodes.has_key (id) :
+                       if id in g_nodes :
                                ps = ps + " " + g_nodes[id].Name()
-               print self.Name() + "(" + ps + " )"
+               print(self.Name() + "(" + ps + " )")
                for id in self.members :
-                       print "\t", g_nodes[id], id
+                       print("\t", g_nodes[id], id)
                        if g_nodes[id].IsMethod() :
-                               print "\t" + g_nodes[id].Signature()
+                               print("\t" + g_nodes[id].Signature())
                        elif g_nodes[id].IsUnion() :
-                               print "\t" + g_nodes[id].Name()
+                               print("\t" + g_nodes[id].Name())
                        else :
                                try :
-                                       print "\t" + g_nodes[id].Name() + ":" + 
g_nodes[g_nodes[id].type].Name()
+                                       print("\t" + g_nodes[id].Name() + ":" + 
g_nodes[g_nodes[id].type].Name())
                                except AttributeError :
-                                       print "AttributeError:", g_nodes[id]
+                                       print("AttributeError:", g_nodes[id])
 
 class Namespace(Node) :
        def __init__ (self, name) :
@@ -179,7 +179,7 @@ class Namespace(Node) :
                self.name = name
        def Name (self) :
                id = self.context
-               if  g_nodes.has_key (id) and g_nodes[id].Name() != "" :
+               if  id in g_nodes and g_nodes[id].Name() != "" :
                        return g_nodes[id].Name() + "::" + self.name
                else :
                        return self.name
@@ -198,22 +198,22 @@ def Parse (sFile, nodes) :
                if name in ["Class", "Struct"] :
                        #print attrs["name"], attrs["id"]
                        o = Klass(attrs["name"])
-                       if attrs.has_key("bases") :
-                               bs = string.split (attrs["bases"], " ")
+                       if "bases" in attrs :
+                               bs = " ".split (attrs["bases"])
                                for s in bs :
                                        o.AddParent (s)
-                       if attrs.has_key("members") :
-                               ms = string.split (attrs["members"], " ")
+                       if "members" in attrs :
+                               ms = " ".split (attrs["members"])
                                for s in ms :
                                        if s != "" :
                                                o.AddMember (s)
-                       if attrs.has_key("abstract") :
-                               o.abstract = string.atoi(attrs["abstract"])
+                       if "abstract" in attrs :
+                               o.abstract = int(attrs["abstract"])
                        g_classes.append (o)
                elif "Union" == name :
                        o = Union(attrs["name"])
-                       if attrs.has_key("members") :
-                               ms = string.split (attrs["members"], " ")
+                       if "members" in attrs :
+                               ms = attrs["members"].split (" ")
                                for s in ms :
                                        if s != "" :
                                                o.AddMember (s)
@@ -229,13 +229,13 @@ def Parse (sFile, nodes) :
                        elif "Destructor" == name : o = Method ("~" + attrs["name"], None)
                        else : o = Method (attrs["name"], attrs["returns"])
 
-                       if attrs.has_key("virtual") : o.virtual += string.atoi(attrs["virtual"])
-                       if attrs.has_key("pure_virtual") : o.virtual += string.atoi(attrs["pure_virtual"])
-                       if attrs.has_key("access") : o.access = attrs["access"]
+                       if "virtual" in attrs : o.virtual += int(attrs["virtual"])
+                       if "pure_virtual" in attrs : o.virtual += int(attrs["pure_virtual"])
+                       if "access" in attrs : o.access = attrs["access"]
                elif name in ["Field", "Typedef"] :
                        o = Argument (attrs["name"], attrs["type"])
-                       if attrs.has_key("access") : o.access = attrs["access"]
-                       if attrs.has_key("static") : o.static = attrs["static"]
+                       if "access" in attrs : o.access = attrs["access"]
+                       if "static" in attrs : o.static = attrs["static"]
                elif name in ["FundamentalType", "Enumeration"] :
                        o = Type (attrs["name"])
                elif "ReferenceType" == name :
@@ -247,7 +247,7 @@ def Parse (sFile, nodes) :
                elif "CvQualifiedType" == name :
                        o = Fassade (attrs["type"], "const", "")
                elif "Argument" == name :
-                       if attrs.has_key("name") :
+                       if "name" in attrs :
                                o = Argument (attrs["name"], attrs["type"])
                        else :
                                o = Fassade (attrs["type"], "", "")
@@ -263,7 +263,7 @@ def Parse (sFile, nodes) :
                        if ctx[-1][1] :
                                ctx[-1][1].AddName (attrs["name"])
                elif name in ["Function", "OperatorFunction", "FunctionType"] :
-                       if attrs.has_key("name") :
+                       if "name" in attrs :
                                o = Function (attrs["returns"], attrs["name"])
                        else : # function & type
                                o = Function (attrs["returns"], attrs["id"])
@@ -272,9 +272,9 @@ def Parse (sFile, nodes) :
                elif "File" == name :
                        pass # FIXME: thrown away
                else :
-                       print "Unhandled:", name
+                       print("Unhandled:", name)
                if o :
-                       if attrs.has_key("context") :
+                       if "context" in attrs :
                                #print attrs["context"]
                                o.context = attrs["context"]
                        nodes[attrs["id"]] = o
@@ -319,7 +319,7 @@ def ImportXml (sFile, diagramData) :
                bUsed = 0
                for p in c.Parents() :
                        if c.Name()[:5] == "std::" : continue # is this too drastic ?
-                       if theLinks.has_key(p) :
+                       if p in theLinks :
                                theLinks[p] += 1
                                bUsed = 1
                if bUsed :
@@ -328,17 +328,17 @@ def ImportXml (sFile, diagramData) :
                        nTotal += 1
        if nTotal < 2 : # arbitrary limit to generate simple diagrams not using inheritance at all
                for c in g_classes :
-                       if theLinks.has_key(c.Name) :
+                       if c.Name in theLinks :
                                theLinks[c.Name()] += 1
        # now everything interesting should be in theLinks with a 'ref count' above zero
        for c in g_classes :
-               if not theLinks.has_key(c.Name()) : continue
+               if c.Name() not in theLinks : continue
                if theLinks[c.Name()] :
                        theLinks[c.Name()] = c
                else :
                        del theLinks[c.Name()]
        theObjects = {}
-       for s in theLinks.keys() :
+       for s in list(theLinks.keys()) :
                o, h1, h2 = dia.get_object_type("UML - Class").create(0,0)
                layer.add_object(o)
                o.properties["name"] = s.encode("UTF-8")
@@ -348,7 +348,7 @@ def ImportXml (sFile, diagramData) :
                attributes = []
                c = theLinks[s]
                for mid in c.members :
-                       if not g_nodes.has_key(mid) :
+                       if mid not in g_nodes :
                                continue #HACK
                        m = g_nodes[mid]
                        #print m
@@ -357,7 +357,7 @@ def ImportXml (sFile, diagramData) :
                                for a in m.params :
                                        # (name, type, value, comment, kind)
                                        try  :
-                                               print a.name, a.Type()
+                                               print(a.name, a.Type())
                                                params.append ((a.name.encode("UTF-8"), 
a.Type().encode("UTF-8"), None, "", 0))
                                        except :
                                                pass
@@ -368,14 +368,14 @@ def ImportXml (sFile, diagramData) :
                                try  :
                                        attributes.append ((m.Name().encode("UTF-8"), 
m.Type().encode("UTF-8"), "", "", m.Visibility(),0,m.static))
                                except  :
-                                       print "Error", m.name
+                                       print("Error", m.name)
                # set some properties
                o.properties["operations"] = methods
                o.properties["attributes"] = attributes
 
                theObjects[s] = o
        # class connections
-       for s in theLinks.keys() :
+       for s in list(theLinks.keys()) :
                o1 = theObjects[s]
                c = theLinks[s]
                for p in c.Parents() :
diff --git a/plug-ins/python/diamodule.c b/plug-ins/python/diamodule.c
index c412a9270..75d85cf32 100644
--- a/plug-ins/python/diamodule.c
+++ b/plug-ins/python/diamodule.c
@@ -22,6 +22,7 @@
 #include <Python.h>
 #include <locale.h>
 
+#include "pydia.h"
 #include "pydia-diagram.h"
 #include "pydia-display.h"
 #include "pydia-layer.h"
@@ -158,6 +159,7 @@ PyDia_GetObjectType(PyObject *self, PyObject *args)
     return NULL;
 }
 
+
 /*
  * A dictionary interface to all registered object(-types)
  */
@@ -166,19 +168,23 @@ _ot_item (gpointer key,
           gpointer value,
           gpointer user_data)
 {
-    gchar *name = (gchar *)key;
-    DiaObjectType *type = (DiaObjectType *)value;
-    PyObject *dict = (PyObject *)user_data;
-    PyObject *k, *v;
-
-    k = PyString_FromString(name);
-    v = PyDiaObjectType_New(type);
-    if (k && v)
-        PyDict_SetItem(dict, k, v);
-    Py_XDECREF(k);
-    Py_XDECREF(v);
+  char *name = (char *)key;
+  DiaObjectType *type = (DiaObjectType *) value;
+  PyObject *dict = (PyObject *) user_data;
+  PyObject *k, *v;
+
+  k = PyUnicode_FromString (name);
+  v = PyDiaObjectType_New (type);
+
+  if (k && v) {
+    PyDict_SetItem (dict, k, v);
+  }
+
+  Py_XDECREF (k);
+  Py_XDECREF (v);
 }
 
+
 static PyObject *
 PyDia_RegisterPlugin(PyObject *self, PyObject *args)
 {
@@ -581,49 +587,51 @@ static PyMethodDef dia_methods[] = {
 };
 
 
-PyDoc_STRVAR (dia_module_doc,
-              "The dia module allows to write Python plug-ins for Dia "
-              "[https://wiki.gnome.org/Apps/Dia/Python]\n";
-              "\n"
-              "This modules is designed to run Python scripts embedded in Dia."
-              " To make your script accessible\n"
-              "to Dia you have to put it into $HOME/.dia/python and let it "
-              "call one of the register_*() functions.\n"
-              "It is possible to write import filters [register_import()] and"
-              " export filters [register_export()], as well as scripts to "
-              "manipulate existing diagrams or create new ones"
-              " [register_action()].\n");
-
-
-DL_EXPORT (void) initdia (void);
-
-
-#define ADD_TYPE(Name)                                                  \
-  {                                                                     \
-    if (PyType_Ready (&PyDia##Name##_Type) != 0) {                      \
-      g_critical ("Failed to register PyDia##Name##_Type");             \
-    }                                                                   \
-                                                                        \
-    Py_INCREF (&PyDia##Name##_Type);                                    \
-    if (PyModule_AddObject (module,                                     \
-                            #Name,                                      \
-                            (PyObject *) &PyDia##Name##_Type) < 0) {    \
-      Py_DECREF (&PyDia##Name##_Type);                                  \
-      Py_DECREF (module);                                               \
-                                                                        \
-      g_critical ("Failed to add PyDia##Name##_Type");                  \
-    }                                                                   \
+static struct PyModuleDef dia_module_def = {
+  PyModuleDef_HEAD_INIT,
+  "dia",
+  "The dia module allows to write Python plug-ins for Dia "
+  "[https://wiki.gnome.org/Apps/Dia/Python]\n";
+  "\n"
+  "This modules is designed to run Python scripts embedded in Dia."
+  " To make your script accessible\n"
+  "to Dia you have to put it into $HOME/.dia/python and let it "
+  "call one of the register_*() functions.\n"
+  "It is possible to write import filters [register_import()] and"
+  " export filters [register_export()], as well as scripts to "
+  "manipulate existing diagrams or create new ones"
+  " [register_action()].\n",
+  -1,
+  dia_methods,
+};
+
+
+#define ADD_TYPE(Name)                                                       \
+  {                                                                          \
+    if (PyType_Ready (&PyDia##Name##_Type) < 0) {                            \
+      g_critical ("Failed to register PyDia" #Name "_Type (PyType_Ready)");  \
+    }                                                                        \
+                                                                             \
+    Py_INCREF (&PyDia##Name##_Type);                                         \
+    if (PyModule_AddObject (module,                                          \
+                            #Name,                                           \
+                            (PyObject *) &PyDia##Name##_Type) < 0) {         \
+      Py_DECREF (&PyDia##Name##_Type);                                       \
+      Py_DECREF (module);                                                    \
+                                                                             \
+      g_critical ("Failed to add PyDia" #Name "_Type (PyModule_AddObject)"); \
+    }                                                                        \
   }
 
 
 PyMODINIT_FUNC
-initdia (void)
+PyInit_dia (void)
 {
   PyObject *module;
 
   PyDiaDiagram_Type.tp_base = &PyDiaDiagramData_Type,
 
-  module = Py_InitModule3 ("dia", dia_methods, dia_module_doc);
+  module = PyModule_Create (&dia_module_def);
 
   ADD_TYPE (Display);
   ADD_TYPE (Layer);
@@ -650,10 +658,16 @@ initdia (void)
   ADD_TYPE (Menuitem);
   ADD_TYPE (Sheet);
 
+
   if (PyErr_Occurred ()) {
+    PyErr_Print ();
     Py_FatalError ("can't initialize module dia");
+
+    return NULL;
   } else {
     /* should all be no-ops when used embedded */
     libdia_init (DIA_MESSAGE_STDERR);
   }
+
+  return module;
 }
diff --git a/plug-ins/python/diastddia.py b/plug-ins/python/diastddia.py
index e04b8e4e0..9884131f4 100644
--- a/plug-ins/python/diastddia.py
+++ b/plug-ins/python/diastddia.py
@@ -313,7 +313,7 @@ class StandardDiaRenderer :
                fname = image.filename
                # do something better than absolute pathes
                common = os.path.commonprefix ([fname, self.filename])
-               if len(common) > 0 and string.find(self.filename[len(common):], os.path.pathsep) == -1 :
+               if len(common) > 0 and self.filename[len(common):].find(os.path.pathsep) == -1 :
                        fname = fname[len(common):]
                # ensure <broken> does not get through
                fname = fname.replace("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
diff --git a/plug-ins/python/diasvg.py b/plug-ins/python/diasvg.py
index 99ac4b433..05ae6562d 100644
--- a/plug-ins/python/diasvg.py
+++ b/plug-ins/python/diasvg.py
@@ -74,20 +74,20 @@ class SvgRenderer :
        def draw_object (self, object, matrix) :
                self.f.write("<!-- " + object.type.name + " -->\n")
                odict = object.properties["meta"].value
-               if odict.has_key("url") :
+               if "url" in odict :
                        self.f.write('<a xlink:href="' + self._escape(odict["url"]) + '">\n')
-               if odict.has_key("id") or matrix :
+               if "id" in odict or matrix :
                        attrs = ''
                        if matrix :
                                attrs += 'transform="matrix' + str(matrix) + '" '
-                       if odict.has_key("id") :
+                       if "id" in odict :
                                attrs += 'id="' + self._escape(odict['id']) + '"'
                        self.f.write('<g ' + attrs + '>\n')
                # don't forget to render the object
                object.draw (self)
-               if odict.has_key("id") or matrix :
+               if "id" in odict or matrix :
                        self.f.write('</g>\n')
-               if odict.has_key("url") :
+               if "url" in odict :
                        self.f.write('</a>\n')
        def set_linewidth (self, width) :
                if width < 0.001 : # zero line width is invisible ?
@@ -207,7 +207,7 @@ class SvgRenderer :
        def _escape (self, text) :
                # avoid writing XML special characters (ampersand must be first to not break the rest)
                for rep in [('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;'), ('"', '&quot;'), ("'", '&apos;')] :
-                       text = string.replace (text, rep[0], rep[1])
+                       text = text.replace (rep[0], rep[1])
                return text
        def _rgb(self, color) :
                # given a dia color convert to svg color string
diff --git a/plug-ins/python/diasvg_import.py b/plug-ins/python/diasvg_import.py
index a9e364b80..48b8ec283 100644
--- a/plug-ins/python/diasvg_import.py
+++ b/plug-ins/python/diasvg_import.py
@@ -73,7 +73,7 @@ def Color(s) :
        if m :
                return (int(m.group(1)) / 255.0, int(m.group(2)) / 255.0, int(m.group(3)) / 255.0, 1.0)
        # any more ugly color definitions not compatible with pango_color_parse() ?
-       return string.strip(s)
+       return s.strip()
 def _eval (s, _locals) :
        # eval() can be used to execute aribitray code, see e.g. 
http://bugzilla.gnome.org/show_bug.cgi?id=317637
        # here using *any* builtins is an abuse
@@ -84,7 +84,7 @@ def _eval (s, _locals) :
                        import dia
                        dia.message(2, "***Possible exploit attempt***:\n" + s)
                except ImportError :
-                       print "***Possible exploit***:", s
+                       print("***Possible exploit***:", s)
        return None
 class Object :
        def __init__(self) :
@@ -92,14 +92,14 @@ class Object :
                self.translation = None
                # "line_width", "line_colour", "line_style"
        def style(self, s) :
-               sp1 = string.split(s, ";")
+               sp1 = ";".split(s)
                for s1 in sp1 :
-                       sp2 = string.split(string.strip(s1), ":")
+                       sp2 = s1.strip().split(":")
                        if len(sp2) == 2 :
                                try :
-                                       _eval("self." + string.replace(sp2[0], "-", "_") + "(\"" + 
string.strip(sp2[1]) + "\")", locals())
+                                       _eval("self." + sp2[0].replace("-", "_") + "(\"" + sp2[1].strip() + 
"\")", locals())
                                except AttributeError :
-                                       self.props[sp2[0]] = string.strip(sp2[1])
+                                       self.props[sp2[0]] = sp2[1].strip()
        def x(self, s) :
                self.props["x"] = Scaled(s)
        def y(self, s) :
@@ -118,7 +118,7 @@ class Object :
                self.props["fill-rule"] = s
        def stroke_dasharray(self,s) :
                # just an approximation
-               sp = string.split(s,",")
+               sp = s.split(",")
                n = len(sp)
                if n > 0 :
                        # sp[0] == "none" : # ? stupid generator ?
@@ -148,7 +148,7 @@ class Object :
        def __repr__(self) :
                return self.dt + " : " + str(self.props)
        def Dump(self, indent) :
-               print " " * indent, self
+               print(" " * indent, self)
        def Set(self, d) :
                pass
        def ApplyProps(self, o) :
@@ -156,15 +156,15 @@ class Object :
        def CopyProps(self, dest) :
                # to be used to inherit group props to childs _before_ they get their own
                # doesn't use the member functions to avoid scaling once more
-               for p in self.props.keys() :
+               for p in list(self.props.keys()) :
                        dest.props[p] = self.props[p]
        def Create(self) :
                ot = dia.get_object_type (self.dt)
                o, h1, h2 = ot.create(self.props["x"], self.props["y"])
                # apply common props
-               if self.props.has_key("stroke-width") and o.properties.has_key("line_width") :
+               if "stroke-width" in self.props and "line_width" in o.properties :
                        o.properties["line_width"] = self.props["stroke-width"]
-               if self.props.has_key("stroke") and o.properties.has_key("line_colour") :
+               if "stroke" in self.props and "line_colour" in o.properties :
                        if self.props["stroke"] != "none" :
                                try :
                                        o.properties["line_colour"] = Color(self.props["stroke"])
@@ -174,14 +174,14 @@ class Object :
                                        pass
                        else :
                                # Dia can't really display stroke none, some workaround :
-                               if self.props.has_key("fill") and self.props["fill"] != "none" :
+                               if "fill" in self.props and self.props["fill"] != "none" :
                                        #does it really matter ?
                                        try :
                                                o.properties["line_colour"] = Color(self.props["fill"])
                                        except :
                                                pass
                                o.properties["line_width"] = 0.0
-               if self.props.has_key("fill") and o.properties.has_key("fill_colour") :
+               if "fill" in self.props and "fill_colour" in o.properties :
                        if self.props["fill"] == "none" :
                                o.properties["show_background"] = 0
                        else :
@@ -197,9 +197,9 @@ class Object :
                                        # rgb(192,27,38) handled by Color() but ...
                                        # o.properties["fill_colour"] =self.props["fill"]
                                        pass
-               if self.props.has_key("line-style") and o.properties.has_key("line_style") :
+               if "line-style" in self.props and "line_style" in o.properties :
                        o.properties["line_style"] = self.props["line-style"]
-               if self.props.has_key("meta") and o.properties.has_key("meta") :
+               if "meta" in self.props and "meta" in o.properties :
                        o.properties["meta"] = self.props["meta"]
                self.ApplyProps(o)
                return o
@@ -230,7 +230,7 @@ class Svg(Object) :
                global dfUserScale
                global dfViewLength
                self.props["viewBox"] = s
-               sp = string.split(s, " ")
+               sp = s.split(" ")
                w = float(sp[2]) - float(sp[0])
                h = float(sp[3]) - float(sp[1])
                # FIXME: the following relies on the call order of width,height,viewBox
@@ -274,15 +274,15 @@ class Style(Object) :
                        p3 = 0 # ... closing
                        s = self.cdata
                        n = len(s) - 1
-                       while 1 :
-                               p1 = string.find(s, ".", p3, n)
-                               p2 = string.find(s, "{", p1+1, n)
-                               p3 = string.find(s, "}", p2+1, n)
+                       while True:
+                               p1 = s.find(".", p3, n)
+                               p2 = s.find("{", p1+1, n)
+                               p3 = s.find("}", p2+1, n)
                                if p1 < 0 or p2 < 0 or p3 < 0 :
                                        break
-                               print s[p1+1:p2-1], s[p2+1:p3]
+                               print(s[p1+1:p2-1], s[p2+1:p3])
                                self.styles[s[p1+1:p2-1]] = s[p2+1:p3]
-               if self.styles.has_key(st) :
+               if st in self.styles :
                        return self.styles[st]
                return ""
        def __repr__(self) :
@@ -324,7 +324,7 @@ class Group(Object) :
                else :
                        return None
        def Dump(self, indent) :
-               print " " * indent, self
+               print(" " * indent, self)
                for o in self.childs :
                        o.Dump(indent + 1)
 
@@ -344,7 +344,7 @@ class Image(Object) :
                if s[:8] == "file:///" :
                        self.props["uri"] = s.encode("UTF-8")
                elif s[:22] == "data:image/png;base64," :
-                       if _imageData.has_key(s[22:]) :
+                       if s[22:] in _imageData :
                                self.props["uri"] = _imageData[s[22:]] # use file reference
                        else :
                                # an ugly temporary file name, on windoze in %TEMP%
@@ -358,15 +358,15 @@ class Image(Object) :
                else :
                        pass #FIXME how to import data into dia ??
        def Create(self) :
-               if not (self.props.has_key("uri") or self.props.has_key("data")) :
+               if not ("uri" in self.props or "data" in self.props) :
                        return None
                return Object.Create(self)
        def ApplyProps(self,o) :
-               if self.props.has_key("width") :
+               if "width" in self.props :
                        o.properties["elem_width"] = self.props["width"]
-               if self.props.has_key("width") :
+               if "width" in self.props :
                        o.properties["elem_height"] = self.props["height"]
-               if self.props.has_key("uri") :
+               if "uri" in self.props :
                        o.properties["image_file"] = self.props["uri"][8:]
 class Line(Object) :
        def __init__(self) :
@@ -438,15 +438,15 @@ class Path(Object) :
                        elif s1 == "" : # too much whitespaces ;-)
                                pass
                        else :
-                               print "Huh?", s1
+                               print("Huh?", s1)
                                break
                        i += 1
        def ApplyProps(self,o) :
                o.properties["bez_points"] = self.pts
        def Dump(self, indent) :
-               print " " * indent, self
+               print(" " * indent, self)
                for t in self.pts :
-                       print " " * indent, t
+                       print(" " * indent, t)
        #def Create(self) :
        #       return None # not yet
 class Rect(Object) :
@@ -491,10 +491,10 @@ class Poly(Object) :
                Object.__init__(self)
                self.dt = None # abstract class !
        def points(self,s) :
-               sp1 = string.split(s)
+               sp1 = s.split()
                pts = []
                for s1 in sp1 :
-                       sp2 = string.split(s1, ",")
+                       sp2 = s1.split(",")
                        if len(sp2) == 2 :
                                pts.append((Scaled(sp2[0]), Scaled(sp2[1])))
                self.props["points"] = pts
@@ -515,7 +515,7 @@ class Text(Object) :
                self.props["font-size"] = 1.0
                # text_font, text_height, text_color, text_alignment
        def Set(self, d) :
-               if self.props.has_key("text") :
+               if "text" in self.props :
                        self.props["text"] += d
                else :
                        self.props["text"] = d
@@ -537,13 +537,13 @@ class Text(Object) :
                self.props["font-family"] = s
        def ApplyProps(self, o) :
                o.properties["text"] = self.props["text"].encode("UTF-8")
-               if self.props.has_key("text-anchor") :
+               if "text-anchor" in self.props :
                        if self.props["text-anchor"] == "middle" : o.properties["text_alignment"] = 1
                        elif self.props["text-anchor"] == "end" : o.properties["text_alignment"] = 2
                        else : o.properties["text_alignment"] = 0
-               if self.props.has_key("fill") :
+               if "fill" in self.props :
                        o.properties["text_colour"] = Color(self.props["fill"])
-               if self.props.has_key("font-size") :
+               if "font-size" in self.props :
                        o.properties["text_height"] = self.props["font-size"]
 class Desc(Object) :
        #FIXME is this useful ?
@@ -551,12 +551,12 @@ class Desc(Object) :
                Object.__init__(self)
                self.dt = "UML - Note"
        def Set(self, d) :
-               if self.props.has_key("text") :
+               if "text" in self.props :
                        self.props["text"] += d
                else :
                        self.props["text"] = d
        def Create(self) :
-               if self.props.has_key("text") :
+               if "text" in self.props :
                        pass
                        #dia.message(0, self.props["text"].encode("UTF-8"))
                return None
@@ -566,12 +566,12 @@ class Title(Object) :
                Object.__init__(self)
                self.dt = "UML - LargePackage"
        def Set(self, d) :
-               if self.props.has_key("text") :
+               if "text" in self.props :
                        self.props["text"] += d
                else :
                        self.props["text"] = d
        def Create(self) :
-               if self.props.has_key("text") :
+               if "text" in self.props :
                        pass
                return None
 class Unknown(Object) :
@@ -592,7 +592,7 @@ class Importer :
                # 3 handler functions
                def start_element(name, attrs) :
                        #print "<" + name + ">"
-                       if 0 == string.find(name, "svg:") :
+                       if 0 == name.find("svg:") :
                                name = name[4:]
                        if len(stack) > 0 :
                                grp = stack[-1]
@@ -605,14 +605,14 @@ class Importer :
                                #FIXME: to take all the style coming with it into account
                                # Dia would need to support layouted text ...
                                txn, txo = ctx[-1]
-                               if attrs.has_key("dy") :
+                               if "dy" in attrs :
                                        txo.Set("" + "\n") # just a new line (best we can do?)
-                               elif attrs.has_key("dx") :
+                               elif "dx" in attrs :
                                        txo.Set(" ")
                                ctx.append((txn, txo)) #push the same object
                                return
                        else :
-                               s = string.capitalize(name) + "()"
+                               s = name.capitalize() + "()"
                                try :
                                        # should be safe to use eval() here, by XML rules it can just be a 
name or would give
                                        # xml.parsers.expat.ExpatError: not well-formed (invalid token)
@@ -627,18 +627,18 @@ class Importer :
                                        o.style(st)
                                        o.props[a] = attrs[a]
                                        continue
-                               ma = string.replace(a, "-", "_")
+                               ma = a.replace("-", "_")
                                # e.g. xlink:href -> xlink__href
-                               ma = string.replace(ma, ":", "__")
+                               ma = ma.replace(":", "__")
                                s = "o." +  ma + "(\"" + attrs[a] + "\")"
                                try :
                                        _eval(s, locals())
-                               except AttributeError, msg :
+                               except AttributeError as msg :
                                        o.props["meta"] = { a : attrs[a] }
-                                       if not self.errors.has_key(msg) :
+                                       if msg not in self.errors :
                                                self.errors[msg] = s
-                               except SyntaxError, msg :
-                                       if not self.errors.has_key(msg) :
+                               except SyntaxError as msg :
+                                       if msg not in self.errors :
                                                self.errors[msg] = s
                        if grp is None :
                                self.objects.append(o)
@@ -665,7 +665,7 @@ class Importer :
                for o in self.objects :
                        try :
                                od = o.Create()
-                       except TypeError, e :
+                       except TypeError as e :
                                od = None
                                dia.message(1, "SVG import limited, consider another importer.\n(Error: " + 
str(e) + ")")
                        if od :
@@ -678,10 +678,10 @@ class Importer :
                                layer.add_object(od)
                # create an 'Unhandled' layer and dump our Unknown
                # create an 'Errors' layer and dump our errors
-               if len(self.errors.keys()) > 0 :
+               if len(list(self.errors.keys())) > 0 :
                        layer = data.add_layer("Errors")
                        s = "To hide the error messages delete or disable the 'Errors' layer\n"
-                       for e in self.errors.keys() :
+                       for e in list(self.errors.keys()) :
                                s = s + str(e) + " -> " + str(self.errors[e]) + "\n"
 
                        o = Text()
@@ -694,8 +694,8 @@ class Importer :
        def Dump(self) :
                for o in self.objects :
                        o.Dump(0)
-               for e in self.errors.keys() :
-                       print e, "->", self.errors[e]
+               for e in list(self.errors.keys()) :
+                       print(e, "->", self.errors[e])
 
 def Test() :
        import sys
diff --git a/plug-ins/python/dot.py b/plug-ins/python/dot.py
index 2035deed0..394aafd51 100644
--- a/plug-ins/python/dot.py
+++ b/plug-ins/python/dot.py
@@ -31,15 +31,15 @@ class DotRenderer :
        def GetName (self, o) :
                "A small helper to turn a dia object into a name"
                s = ""
-               if o.properties.has_key("name") :
+               if "name" in o.properties :
                        s = o.properties["name"].value
-               elif o.properties.has_key("text") :
+               elif "text" in o.properties :
                        s = o.properties["text"].value.text
                if s is None or s == "" :
                        s = str(o)
                return s
        def GetColor (self, o) :
-               if o.properties.has_key("fill_colour") :
+               if "fill_colour" in o.properties :
                        rgb = o.properties["fill_colour"].value
                        return "#%02x%02x%02x" % (rgb.red * 255, rgb.green * 255, rgb.blue * 255)
                return None
@@ -69,7 +69,7 @@ class DotRenderer :
                                        # these are the connection points of the objects. We will have e.g. 
"UML - Dependency"
                                        for n in c.connected :
                                                # we see the connecting lines multiple times, just process 
once
-                                               if self.edges.has_key(str(n)) :
+                                               if str(n) in self.edges :
                                                        continue
                                                self.edges[str(n)] = 1
                                                if not (n.handles[0].connected_to and 
n.handles[1].connected_to) :
@@ -81,7 +81,7 @@ class DotRenderer :
                                                        self.f.write('"%s" -> "%s"\n' % (self.GetName(a), 
self.GetName(b)))
                                                        #self.f.write('"%s" -> "%s"\n' % 
(str(a.properties["text"].value.text), str(b.properties["text"].value.text)))
                                                except :
-                                                       print a, b, " writing connection failed."
+                                                       print(a, b, " writing connection failed.")
                                else :
                                        pass
                self.f.write('}\n')
diff --git a/plug-ins/python/dot2dia.py b/plug-ins/python/dot2dia.py
index ee224f303..8e9ee67a2 100644
--- a/plug-ins/python/dot2dia.py
+++ b/plug-ins/python/dot2dia.py
@@ -19,7 +19,7 @@
 # \file dot2dia.py \brief translate dot ( http://www.graphviz.org/ ) to Dia format
 # \ingroup ImportFilters
 
-import re, string, sys
+import re, sys
 
 import gettext
 _ = gettext.gettext
@@ -28,7 +28,7 @@ _ = gettext.gettext
 keywords = ['node', 'edge', 'graph', 'digraph', 'strict']
 # starts with either a keyword or a node name in quotes.
 # BEWARE: (?<-> ) - negative lookbehind to not find nodes a second time in connection definition (and 
misinterpret the params)
-rDecl = re.compile(r'\s*(?<!-> )(?P<cmd>(?:' + string.join(keywords, ')|(?:') + ')|(?:\w+' + 
')|(?:"[^"]+"))\s+\[(?P<dict>[^\]]+)\];', re.DOTALL | re.MULTILINE)
+rDecl = re.compile(r'\s*(?<!-> )(?P<cmd>(?:' + ')|(?:'.join(keywords) + ')|(?:\w+' + 
')|(?:"[^"]+"))\s+\[(?P<dict>[^\]]+)\];', re.DOTALL | re.MULTILINE)
 # dont assume that all node names are in quotes
 rEdge = re.compile(r'\s*(?P<n1>("[^"]+")|(\w+))\s*->\s*(?P<n2>("[^"]+")|(\w+))\s+\[(?P<dict>[^\]]+)\];*', 
re.DOTALL | re.MULTILINE)
 # a list of key=value
@@ -75,11 +75,11 @@ class Node(Object) :
                'deliver scaled X,Y coordinate'
                x, y = 0.0, 0.0
                try :
-                       xy = string.split(self.parms['pos'], ',')
+                       xy = self.parms['pos'].split(',')
                        x = float(xy[0]) * cmPoints
                        y = float(xy[1]) * cmPoints
                except :
-                       print "No position on '%s'" % (self.name,)
+                       print("No position on '%s'" % (self.name,))
                return x,-y
        def Size(self) :
                'deliver scaled W,H coordinate'
@@ -88,7 +88,7 @@ class Node(Object) :
                        w = float(self.parms['width']) * cmInch #? maybe this is relative to the font size?
                        h = float(self.parms['height']) * cmInch
                except :
-                       print "No size on '%s'" % (self.name,)
+                       print("No size on '%s'" % (self.name,))
                return w,h
 
 ##
@@ -101,13 +101,13 @@ class Edge(Object) :
        def LabelPos (self) :
                x, y = 0.0, 0.0
                try :
-                       xy = string.split(self.parms['lp'], ',')
+                       xy = self.parms['lp'].split(',')
                        x = float(xy[0]) * cmPoints
                        y = float(xy[1]) * cmPoints
                except :
-                       if self.parms.has_key('label') :
+                       if 'label' in self.parms :
                                # should be optional otherwise
-                               print "No label pos on %s" % (self.src + '->' + self.dest,)
+                               print("No label pos on %s" % (self.src + '->' + self.dest,))
                return x, -y
        def Pos (self) :
                # no need to do something smart, it get adjusted anyway
@@ -115,21 +115,21 @@ class Edge(Object) :
        def SetPoints (self, diaobj) :
                'the property to set must match the type'
                pts = []
-               if self.parms.has_key('pos') :
+               if 'pos' in self.parms :
                        s = self.parms['pos']
                        if s[:2] == 'e,' :
-                               sp = string.split(s[2:], " ")
+                               sp = s[2:].split(" ")
                                # apparently the first point is the end? just put it there!
                                sp.append(sp[-1])
                                del sp[0]
                                bp = []
                                for i in range(0, len(sp)) :
-                                       xy = string.split(sp[i].replace("\n", "").replace("\\", ""), ",")
+                                       xy = sp[i].replace("\n", "").replace("\\", "").split(",")
                                        try :
                                                x = float(xy[0]) * cmPoints
                                                y = float(xy[1]) * (-cmPoints)
                                        except ValueError :
-                                               print xy
+                                               print(xy)
                                                continue
                                        bp.append((x,y))
                                        # must convert to _one_ tuple
@@ -140,15 +140,15 @@ class Edge(Object) :
                                                pts.append ((2, bp[0][0], bp[0][1], bp[1][0], bp[1][1], 
bp[2][0], bp[2][1]))
                                                bp = [] # reset
                        if len(bp) > 0 :
-                               print len(bp), "bezier points left!"
+                               print(len(bp), "bezier points left!")
                if len(pts) > 1 :
                        diaobj.properties['bez_points'] = pts
                else :
-                       print "BezPoints", pts
+                       print("BezPoints", pts)
 
 def MergeParms (d, extra) :
-       for k in extra.keys() :
-               if not d.has_key(k) :
+       for k in list(extra.keys()) :
+               if k not in d :
                        d[k] = extra[k]
 
 ##
@@ -162,29 +162,29 @@ def Parse(sFile) :
        if 0 : # debug regex
                dbg = rDecl.findall(s)
                for db in dbg :
-                       print db
+                       print(db)
        for m in rDecl.finditer(s) :
                if m :
                        name = StripQuotes(m.group("cmd"))
                        if name in keywords :
-                               if extra.has_key(name) :
+                               if name in extra :
                                        MergeParms(extra[name], DictFromString(m.group("dict")))
                                else :
                                        extra[name] = DictFromString(m.group("dict"))
                        else : # must be a node
                                n = Node(name, DictFromString(m.group("dict")))
-                               if extra.has_key('node') :
+                               if 'node' in extra :
                                        MergeParms(n.parms, extra['node'])
                                nodes[name] = n
        for m in rEdge.finditer(s) :
                if m :
                        # the names given are not necessarily registered as nodes already
                        defparams = {}
-                       if extra.has_key('node') :
+                       if 'node' in extra :
                                defparams = extra['node']
                        for k in ["n1", "n2"] :
                                name = StripQuotes(m.group(k))
-                               if nodes.has_key(name) :
+                               if name in nodes :
                                        pass # defparms should be set above
                                else :
                                        nodes[name] = Node(name, defparams)
@@ -217,7 +217,7 @@ def ImportFile (sFile, diagramData) :
        """ read the dot file and create diagram objects """
        nodes, edges = Parse(sFile)
        layer = diagramData.active_layer # can do better, e.g. layer per graph
-       for key in nodes.keys() :
+       for key in list(nodes.keys()) :
                n = nodes[key]
                nodeType = dia.get_object_type(n.typename) # could be optimized out of loop
                x, y = n.Pos()
@@ -227,11 +227,11 @@ def ImportFile (sFile, diagramData) :
                # obj.move_handle(h2, (x+w/2, y+h/2), 0, 0) # resize the object
                obj.properties["elem_width"] = w
                obj.properties["elem_height"] = h
-               if n.parms.has_key('fillcolor') :
+               if 'fillcolor' in n.parms :
                        try :
                                obj.properties['fill_colour'] = n.parms['fillcolor'] # same color syntax?
                        except :
-                               print "Failed to apply:", n.parms['fillcolor']
+                               print("Failed to apply:", n.parms['fillcolor'])
                layer.add_object(obj)
                AddLabel (layer, (x,y), n.name, n.FontSize(), 1)
                obj.properties['meta'] = n.parms # copy all (remaining) parameters
@@ -242,29 +242,29 @@ def ImportFile (sFile, diagramData) :
                x, y = e.Pos() # just to have a start
                con, h1, h2 = edgeType.create(x,y)
                e.SetPoints(con)
-               if e.parms.has_key('style') : # set line style
+               if 'style' in e.parms : # set line style
                        con.properties['line_style'] = (4, 0.5) #FIXME: hard-coded dotted
-               if e.parms.has_key('weight') :
+               if 'weight' in e.parms :
                        con.properties['line_width'] = float(e.parms['weight']) / 10.0 # arbitray anyway
                layer.add_object(con)
-               if nodes.has_key(e.src) :
+               if e.src in nodes :
                        h = con.handles[0]
                        obj = nodes[e.src]
                        # by moving to the cp position first, the connection's points get recalculated
                        pos = obj.connections[8].pos
                        con.move_handle(h, pos, 0, 0)
                        h.connect(obj.connections[8]) # connect to mid-point
-               if nodes.has_key(e.dest) :
+               if e.dest in nodes :
                        h = con.handles[-1]
                        obj = nodes[e.dest]
                        pos = obj.connections[8].pos
                        con.move_handle(h, pos, 0, 0)
                        h.connect (obj.connections[8]) # connect to mid-point
-               if e.parms.has_key('label') :
+               if 'label' in e.parms :
                        AddLabel (layer, e.LabelPos(), e.parms['label'], e.FontSize())
        diagram = None # FIXME: get it
        if diagram :
-               for n, o in nodes.iteritems() :
+               for n, o in nodes.items() :
                        diagram.update_connections(o)
                diagram.update_extents()
        return diagramData
@@ -272,10 +272,10 @@ def ImportFile (sFile, diagramData) :
 if __name__ == '__main__':
        # just testing at the moment
        nodes, edges = Parse(sys.argv[1])
-       for k, n in nodes.iteritems() :
-               print "Name:", n.name, "Pos:", n.Pos(), "WxH:", n.Size()
+       for k, n in nodes.items() :
+               print("Name:", n.name, "Pos:", n.Pos(), "WxH:", n.Size())
        for e in edges :
-               print e.src, "->", e.dest, e.LabelPos(), e.parms
+               print(e.src, "->", e.dest, e.LabelPos(), e.parms)
 else :
        # run as a Dia plug-in
        import dia
diff --git a/plug-ins/python/doxrev.py b/plug-ins/python/doxrev.py
index 5dc243f03..0aa68fb98 100644
--- a/plug-ins/python/doxrev.py
+++ b/plug-ins/python/doxrev.py
@@ -75,23 +75,23 @@ class Element :
                        self.props[key] = value
        def SetAttrs (self, name, attrs) :
                # just a copy of the tags attributes
-               if self.__class__.__name__ == string.capitalize(name) :
+               if self.__class__.__name__ == name.capitalize():
                        self.attrs = attrs
-               elif len(attrs.keys()) > 0 :
+               elif len(list(attrs.keys())) > 0 :
                        if g_verbose :
-                               print "Ignoring", name, "attrs on", self.kind
+                               print("Ignoring", name, "attrs on", self.kind)
        def Dump (self) :
-               print self.kind, self.name, self.props
+               print(self.kind, self.name, self.props)
 # still no tag, fallback
 class Unknown(Element) :
        def __init__ (self, name, n) :
                self.name = name
                self.kind = name
-               print "Unknown", " " * n, name
+               print("Unknown", " " * n, name)
        def Dump (self) :
                pass
        def Set (self, key, value) :
-               print "???", key, value
+               print("???", key, value)
        def SetAttrs (self, name, attrs) :
                pass
 
@@ -109,14 +109,14 @@ class Compounddef(Element) :
                if name == "compounddef" :
                        self.id = attrs["id"]
                elif name == "basecompoundref" :
-                       if g_verbose : print "Base", attrs["refid"]
+                       if g_verbose : print("Base", attrs["refid"])
                        self.bases.append (attrs["refid"])
                elif name == "derivedcompoundref" :
                        pass
                else :
                        Element.SetAttrs (self, name, attrs)
        def Dump (self) :
-               print self.kind, self.name
+               print(self.kind, self.name)
                for v in self.visibilities :
                        v.Dump ()
        # these functions are dia specific (layout in the lists) alsthough not using dia facilities
@@ -125,7 +125,7 @@ class Compounddef(Element) :
                for v in self.visibilities :
                        if v.__class__.__name__ != "Sectiondef" :
                                if not v.__class__.__name__ in ["Briefdescription", "Detaileddescription", 
"Listofallmembers"] :
-                                       print "***", v.__class__.__name__
+                                       print("***", v.__class__.__name__)
                                continue
                        visibility = v.GetVisibility()
                        value = ""
@@ -143,7 +143,7 @@ class Compounddef(Element) :
                for v in self.visibilities :
                        if v.__class__.__name__ != "Sectiondef" :
                                if not v.__class__.__name__ in ["Briefdescription", "Detaileddescription", 
"Listofallmembers"] :
-                                       print "***", v.__class__.__name__
+                                       print("***", v.__class__.__name__)
                                continue
                        visibility = v.GetVisibility()
                        if visibility is None :
@@ -180,7 +180,7 @@ class Sectiondef(Element) :
        def Add (self, o) :
                self.members.append (o)
        def Dump (self) :
-               print " ", self.kind, ":"
+               print(" ", self.kind, ":")
                for m in self.members :
                        m.Dump ()
        # Dia specifics, map tags/attribs to Dia enums
@@ -191,7 +191,7 @@ class Sectiondef(Element) :
                elif "private" in self.kind : return 2
                elif "user-defined" in self.kind : return 0 # FIXME: sectiondef not really defines visibility
                else :
-                       print self.kind, "visibility?"
+                       print(self.kind, "visibility?")
                        return None
 # e.g. function, enum
 class Memberdef(Element) :
@@ -204,13 +204,13 @@ class Memberdef(Element) :
        def Add (self, o) :
                self.parameters.append (o)
        def Dump (self) :
-               print "\t", self.type, self.name
+               print("\t", self.type, self.name)
                for p in self.parameters :
                        p.Dump ()
        # again Dia dependent, although not using it
        def GetInheritanceType (self) :
                # inheritance_type (0: abstract, 1 : virtual, 2:leaf)
-               if self.attrs and self.attrs.has_key ("virt") :
+               if self.attrs and "virt" in self.attrs :
                        if self.attrs["virt"] == "pure-virtual" :
                                return 0
                        elif self.attrs["virt"] == "non-virtual" :
@@ -218,24 +218,24 @@ class Memberdef(Element) :
                        elif self.attrs["virt"] == "virtual" :
                                return 1
                        else :
-                               print 'virt="' + self.attrs["virt"] + '" ???'
+                               print('virt="' + self.attrs["virt"] + '" ???')
                                return None
                return 0
        def GetVisibility (self) :
-               if self.attrs and self.attrs.has_key ("prot") :
+               if self.attrs and "prot" in self.attrs :
                        if self.attrs["prot"] == "public" : return 0
                        elif self.attrs["prot"] == "protected" : return 1
                        elif self.attrs["prot"] == "private" : return 2
                return None
        def IsStatic (self) :
                # class_scope
-               if self.attrs and self.attrs.has_key ("static") :
+               if self.attrs and "static" in self.attrs :
                        if self.attrs["static"] != "no" :
                                return 1
                return 0
        def IsQuery (self) :
                # const members
-               if self.attrs and self.attrs.has_key ("const") :
+               if self.attrs and "const" in self.attrs :
                        if self.attrs["const"] != "no" :
                                return 1
                return 0
@@ -245,7 +245,7 @@ class Enumvalue(Element) :
                Element.__init__(self)
                self.kind = ""
        def Dump (self) :
-               print "\t" * 2, self.name
+               print("\t" * 2, self.name)
        def Add (self, o) :
                pass # throwing away a comment
 class Param(Element) :
@@ -264,7 +264,7 @@ class Param(Element) :
                for o in self.comments :
                        s += o.text
        def Dump (self) :
-               print "\t" * 2, self.type, self.name
+               print("\t" * 2, self.type, self.name)
 class Parameterlist(Element) :
        def __init__ (self, kind) :
                Element.__init__(self)
@@ -289,7 +289,7 @@ class Comment :
        def SetAttrs (self, name, attrs) :
                pass
        def Dump (self) :
-               print "\t#", self.text
+               print("\t#", self.text)
 class Briefdescription(Comment) :
        def __init__ (self) :
                Comment.__init__(self)
@@ -336,11 +336,11 @@ def Parse (sData) :
        # 3 handler functions
        def start_element(name, attrs) :
                parent = find_object (ctx)
-               s = string.capitalize(name)
+               s = name.capitalize()
                try :
                        o = eval (s + '("' + attrs["kind"] + '")')
                except NameError :
-                       print "Handle?", s
+                       print("Handle?", s)
                        o = Unknown(name, len(ctx))
                except KeyError :
                        try :
@@ -359,11 +359,11 @@ def Parse (sData) :
                        try :
                                parent.Add (o)
                        except AttributeError :
-                               print "Handle(?)", parent, "Add(", o, ")"
+                               print("Handle(?)", parent, "Add(", o, ")")
                                pass
        def char_data(data) :
                # we are not interested in pure whitespace
-               data = string.strip (data)
+               data = data.strip ()
                if len(data) == 0 :
                        return
                try :
@@ -373,8 +373,8 @@ def Parse (sData) :
                                #print "\t" * 4, o, "<", name, data, ">"
                                o.Set (name, data)
                        else :
-                               if not unhandled.has_key (ctx[-1][0]) :
-                                       print "ToDo?", ctx[-1][0]
+                               if ctx[-1][0] not in unhandled :
+                                       print("ToDo?", ctx[-1][0])
                                        unhandled[ctx[-1][0]] = 1
                                #ctx[-1][1].Set (data)
                except KeyError :
@@ -402,7 +402,7 @@ def GetClasses (files) :
                try :
                        classes.extend (Parse (f.read()))
                except xml.parsers.expat.ExpatError :
-                       print "XML Error:", s
+                       print("XML Error:", s)
        # before adding them to the diagram sort by inheritance (less first)
        return classes
 
@@ -424,7 +424,7 @@ XML_PROGRAMLISTING = NO
        p = os.popen ("doxygen doxyfile.rev")
        s = p.readline()
        while s :
-               print s[:-1]
+               print(s[:-1])
                s = p.readline()
        p.close ()
 
@@ -434,8 +434,8 @@ if __name__ == '__main__':
        classes = GetClasses (files)
        for o in classes :
                o.Dump()
-               print o.GetMethods()
-               print o.GetAttributes()
+               print(o.GetMethods())
+               print(o.GetAttributes())
 else :
        import dia
 
@@ -452,19 +452,19 @@ else :
                        o.properties["abstract"] = c.IsAbstract ()
                        try :
                                o.properties["operations"] = c.GetMethods()
-                       except TypeError, msg :
-                               print "Failed assigning 'operations':", msg, c.GetMethods()
+                       except TypeError as msg :
+                               print("Failed assigning 'operations':", msg, c.GetMethods())
                        o.properties["attributes"] = c.GetAttributes()
 
                        layer.add_object(o)
                # create the links (inhertiance, maybe containement, refernces, factory, ...)
-               for s in class_map.keys () :
+               for s in list(class_map.keys ()) :
                        c, o = class_map[s]
-                       print s, c, c.bases
+                       print(s, c, c.bases)
                        for b in c.bases :
-                               if class_map.has_key (b) :
+                               if b in class_map :
                                        k, p = class_map[b]
-                                       print c.id, "->", k.id
+                                       print(c.id, "->", k.id)
                                        con, h1, h2 = dia.get_object_type("UML - Generalization").create(0,0)
                                        layer.add_object (con)
                                        h1.connect (p.connections[6])
diff --git a/plug-ins/python/export-object.py b/plug-ins/python/export-object.py
index 07dd98446..5327b387f 100644
--- a/plug-ins/python/export-object.py
+++ b/plug-ins/python/export-object.py
@@ -29,7 +29,7 @@ class ObjRenderer :
                                self.f.write ("\t"*2 + "</connections>\n")
 
                                self.f.write ("\t"*2 + str(o.properties) + "\n")
-                               keys = o.properties.keys()
+                               keys = list(o.properties.keys())
                                for s in keys :
                                        self.f.write ("\t"*3 + str(o.properties[s]) + "\n")
                                        if o.properties[s].type == "string" :
diff --git a/plug-ins/python/gtkcons.py b/plug-ins/python/gtkcons.py
index beda61584..990777fa1 100755
--- a/plug-ins/python/gtkcons.py
+++ b/plug-ins/python/gtkcons.py
@@ -27,322 +27,372 @@
 # As well as the starting attributes in namespace, the session will also
 # have access to the list __history__, which is the command history.
 
-import sys, string, traceback
+import sys, traceback
 
-# Make sure we use pygtk for gtk 2.0
-import pygtk
-pygtk.require("2.0")
+import gi
 
-import gtk
-import gtk.keysyms
-import gobject
-import pango
+gi.require_version('Pango', '1.0')
+gi.require_version('Gtk', '2.0')
+
+from gi.repository import GObject, Pango, Gtk, Gdk
 
 import gettext
+
 _ = gettext.gettext
 
 stdout = sys.stdout
 
-if not hasattr(sys, 'ps1'): sys.ps1 = '>>> '
-if not hasattr(sys, 'ps2'): sys.ps2 = '... '
+if not hasattr(sys, "ps1"):
+    sys.ps1 = ">>> "
+if not hasattr(sys, "ps2"):
+    sys.ps2 = "... "
 
 # some functions to help recognise breaks between commands
 def remQuotStr(s):
-       '''Returns s with any quoted strings removed (leaving quote marks)'''
-       r = ''
-       inq = 0
-       qt = ''
-       prev = '_'
-       while len(s):
-               s0, s = s[0], s[1:]
-               if inq and (s0 != qt or prev == '\\'):
-                       prev = s0
-                       continue
-               prev = s0
-               if s0 in '\'"':
-                       if inq:
-                               inq = 0
-                       else:
-                               inq = 1
-                               qt = s0
-               r = r + s0
-       return r
+    """Returns s with any quoted strings removed (leaving quote marks)"""
+    r = ""
+    inq = 0
+    qt = ""
+    prev = "_"
+    while len(s):
+        s0, s = s[0], s[1:]
+        if inq and (s0 != qt or prev == "\\"):
+            prev = s0
+            continue
+        prev = s0
+        if s0 in "'\"":
+            if inq:
+                inq = 0
+            else:
+                inq = 1
+                qt = s0
+        r = r + s0
+    return r
+
 
 def bracketsBalanced(s):
-       '''Returns true iff the brackets in s are balanced'''
-       s = filter(lambda x: x in '()[]{}', s)
-       stack = []
-       brackets = {'(':')', '[':']', '{':'}'}
-       while len(s) != 0:
-               if s[0] in ")]}":
-                       if len(stack) != 0 and brackets[stack[-1]] == s[0]:
-                               del stack[-1]
-                       else:
-                               return 0
-               else:
-                       stack.append(s[0])
-               s = s[1:]
-       return len(stack) == 0
+    """Returns true iff the brackets in s are balanced"""
+    s = [x for x in s if x in "()[]{}"]
+    stack = []
+    brackets = {"(": ")", "[": "]", "{": "}"}
+    while len(s) != 0:
+        if s[0] in ")]}":
+            if len(stack) != 0 and brackets[stack[-1]] == s[0]:
+                del stack[-1]
+            else:
+                return 0
+        else:
+            stack.append(s[0])
+        s = s[1:]
+    return len(stack) == 0
+
 
 class gtkoutfile:
-       '''A fake output file object.  It sends output to a TK test widget,
-       and if asked for a file number, returns one set on instance creation'''
-       def __init__(self, w, fn, tag):
-               self.__fn = fn
-               self.__w = w
-               self.__tag = tag
-       def close(self): pass
-       flush = close
-       def fileno(self):    return self.__fn
-       def isatty(self):    return 0
-       def read(self, a):   return ''
-       def readline(self):  return ''
-       def readlines(self): return []
-       def write(self, s):
-               #stdout.write(str(self.__w.get_point()) + '\n')
-               self.__w.text_insert(self.__tag, s)
-       def writelines(self, l):
-               self.__w.text_insert(self.__tag, l)
-       def seek(self, a):   raise IOError, (29, 'Illegal seek')
-       def tell(self):      raise IOError, (29, 'Illegal seek')
-       truncate = tell
-
-class Console(gtk.VBox):
-       def __init__(self, namespace={}, copyright='', quit_cb=None):
-               gtk.VBox.__init__(self, spacing=2)
-               self.copyright = copyright
-
-               self.quit_cb = quit_cb
-
-               self.inp = gtk.HBox()
-               self.pack_start(self.inp)
-               self.inp.show()
-
-               self.scroll = gtk.ScrolledWindow()
-               self.inp.pack_start(self.scroll)
-               self.scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
-               self.scroll.show()
-
-               self.text = gtk.TextView()
-               self.text.modify_font(pango.FontDescription("monospace 8"))
-               self.text.set_editable(False)
-               self.text.set_wrap_mode (gtk.WRAP_WORD)
-               self.text.set_size_request(500, 400)
-               self.scroll.add(self.text)
-               self.text.show()
-
-               buffer = self.text.get_buffer()
-               self.normal = buffer.create_tag("normal")
-               self.title = buffer.create_tag("title")
-               self.title.set_property("weight", 600)
-               self.error = buffer.create_tag("error")
-               self.error.set_property("foreground", "red")
-               self.command = buffer.create_tag('command')
-               self.command.set_property("weight", 600)
-               self.command.set_property("foreground", "blue")
-
-               self.inputbox = gtk.HBox(spacing=2)
-               self.pack_end(self.inputbox, expand=False)
-               self.inputbox.show()
-
-               self.prompt = gtk.Label(sys.ps1)
-               self.prompt.set_padding(2, 0)
-               self.prompt.set_size_request(26, -1)
-               self.inputbox.pack_start(self.prompt, fill=False, expand=False)
-               self.prompt.show()
-
-               self.line = gtk.Entry()
-               self.line.set_size_request(400,-1)
-               self.line.connect("key_press_event", self.key_function)
-               self.inputbox.pack_start(self.line, padding=2)
-               self.line.show()
-
-               # now let the text box be resized
-               self.text.set_size_request(0, 0)
-               self.line.set_size_request(0, -1)
-
-               self.namespace = namespace
-
-               self.cmd = ''
-               self.cmd2 = ''
-
-               # set up hooks for standard output.
-               self.stdout = gtkoutfile(self, sys.stdout.fileno(),
-                                       self.normal)
-               try :
-                       # this will mostly fail on win32 ...
-                       self.stderr = gtkoutfile(self, sys.stderr.fileno(),
-                                               self.error)
-               except :
-                       # ... but gtkoutfile is not using the fileno anyway
-                       self.stderr = gtkoutfile(self, -1,
-                                               self.error)
-               # set up command history
-               self.history = ['']
-               self.histpos = 0
-               self.namespace['__history__'] = self.history
-
-       def scroll_to_end (self):
-               iter = self.text.get_buffer().get_end_iter()
-               self.text.scroll_to_iter(iter, 0.0)
-               return False  # don't requeue this handler
-
-       def text_insert(self, tag, s) :
-               buffer = self.text.get_buffer()
-               iter = buffer.get_end_iter()
-               buffer.insert_with_tags (iter, s, tag)
-               gobject.idle_add(self.scroll_to_end)
-
-       def init(self):
-               self.text.realize()
-               #??? self.text.style = self.text.get_style()
-               self.text.fg = self.text.style.fg[gtk.STATE_NORMAL]
-               self.text.bg = self.text.style.white
-
-               self.text_insert (self.title, 'Python %s\n%s\n\n' %
-                                (sys.version, sys.copyright) +
-                                'Interactive Python-GTK Console - ' +
-                                'Copyright (C) 1998 James Henstridge\n\n' +
-                                self.copyright + '\n')
-               self.line.grab_focus()
-
-       def quit(self, *args):
-               self.destroy()
-               if self.quit_cb: self.quit_cb()
-
-       def key_function(self, entry, event):
-               if event.keyval == gtk.keysyms.Return:
-                       self.line.emit_stop_by_name("key_press_event")
-                       self.eval()
-               if event.keyval == gtk.keysyms.Tab:
-                       self.line.emit_stop_by_name("key_press_event")
-                       self.line.append_text('\t')
-                       gobject.idle_add(self.focus_text)
-               elif event.keyval in (gtk.keysyms.KP_Up, gtk.keysyms.Up):
-                       self.line.emit_stop_by_name("key_press_event")
-                       self.historyUp()
-                       gobject.idle_add(self.focus_text)
-               elif event.keyval in (gtk.keysyms.KP_Down, gtk.keysyms.Down):
-                       self.line.emit_stop_by_name("key_press_event")
-                       self.historyDown()
-                       gobject.idle_add(self.focus_text)
-               elif event.keyval in (gtk.keysyms.D, gtk.keysyms.d) and \
-                    event.state & gtk.gdk.CONTROL_MASK:
-                       self.line.emit_stop_by_name("key_press_event")
-                       self.ctrld()
-
-       def focus_text(self):
-               self.line.grab_focus()
-               return False  # don't requeue this handler
-
-       def ctrld(self):
-               self.quit()
-               pass
-
-       def historyUp(self):
-               if self.histpos > 0:
-                       l = self.line.get_text()
-                       if len(l) > 0 and l[0] == '\n': l = l[1:]
-                       if len(l) > 0 and l[-1] == '\n': l = l[:-1]
-                       self.history[self.histpos] = l
-                       self.histpos = self.histpos - 1
-                       self.line.set_text(self.history[self.histpos])
-
-       def historyDown(self):
-               if self.histpos < len(self.history) - 1:
-                       l = self.line.get_text()
-                       if len(l) > 0 and l[0] == '\n': l = l[1:]
-                       if len(l) > 0 and l[-1] == '\n': l = l[:-1]
-                       self.history[self.histpos] = l
-                       self.histpos = self.histpos + 1
-                       self.line.set_text(self.history[self.histpos])
-
-       def eval(self):
-               l = self.line.get_text() + '\n'
-               if len(l) > 1 and l[0] == '\n': l = l[1:]
-               self.histpos = len(self.history) - 1
-               if len(l) > 0 and l[-1] == '\n':
-                       self.history[self.histpos] = l[:-1]
-               else:
-                       self.history[self.histpos] = l
-               self.line.set_text('')
-               self.text_insert(self.command, self.prompt.get() + l)
-               if l == '\n':
-                       self.run(self.cmd)
-                       self.cmd = ''
-                       self.cmd2 = ''
-                       return
-               self.histpos = self.histpos + 1
-               self.history.append('')
-               self.cmd = self.cmd + l
-               self.cmd2 = self.cmd2 + remQuotStr(l)
-               l = string.rstrip(l)
-               if not bracketsBalanced(self.cmd2) or l[-1] == ':' or \
-                               l[-1] == '\\' or l[0] in ' \11':
-                       self.prompt.set_text(sys.ps2)
-                       self.prompt.queue_draw()
-                       return
-               self.run(self.cmd)
-               self.cmd = ''
-               self.cmd2 = ''
-
-       def run(self, cmd):
-               sys.stdout, self.stdout = self.stdout, sys.stdout
-               sys.stderr, self.stderr = self.stderr, sys.stderr
-               try:
-                       try:
-                               r = eval(cmd, self.namespace, self.namespace)
-                               if r is not None:
-                                       print `r`
-                       except SyntaxError:
-                               exec cmd in self.namespace
-               except:
-                       if hasattr(sys, 'last_type') and \
-                                       sys.last_type == SystemExit:
-                               self.quit()
-                       else:
-                               traceback.print_exc()
-               self.prompt.set_text(sys.ps1)
-               self.prompt.queue_draw()
-               #adj = self.text.get_vadjustment()
-               #adj.set_value(adj.upper - adj.page_size)
-               sys.stdout, self.stdout = self.stdout, sys.stdout
-               sys.stderr, self.stderr = self.stderr, sys.stderr
-
-def gtk_console(ns, title='Python', copyright='', menu=None):
-       win = gtk.Window()
-       win.set_default_size(475, 300)
-       #win.connect("destroy", mainquit)
-       win.set_title(title)
-       def quit(win=win):
-               win.destroy()
-       cons = Console(namespace=ns, copyright=copyright, quit_cb=quit)
-       if menu:
-               box = gtk.VBox()
-               win.add(box)
-               box.show()
-               box.pack_start(menu, expand=False)
-               menu.show()
-               box.pack_start(cons)
-       else:
-               win.add(cons)
-       cons.show()
-       win.show()
-       cons.init()
+    """A fake output file object.  It sends output to a TK test widget,
+    and if asked for a file number, returns one set on instance creation"""
+
+    def __init__(self, w, fn, tag):
+        self.__fn = fn
+        self.__w = w
+        self.__tag = tag
+
+    def close(self):
+        pass
+
+    flush = close
+
+    def fileno(self):
+        return self.__fn
+
+    def isatty(self):
+        return 0
+
+    def read(self, a):
+        return ""
+
+    def readline(self):
+        return ""
+
+    def readlines(self):
+        return []
+
+    def write(self, s):
+        # stdout.write(str(self.__w.get_point()) + '\n')
+        self.__w.text_insert(self.__tag, s)
+
+    def writelines(self, l):
+        self.__w.text_insert(self.__tag, l)
+
+    def seek(self, a):
+        raise IOError(29, "Illegal seek")
+
+    def tell(self):
+        raise IOError(29, "Illegal seek")
+
+    truncate = tell
+
+
+class Console(Gtk.VBox):
+    def __init__(self, namespace={}, copyright="", quit_cb=None):
+        GObject.GObject.__init__(self, spacing=2)
+        self.copyright = copyright
+
+        self.quit_cb = quit_cb
+
+        self.inp = Gtk.HBox()
+        self.pack_start(self.inp, True, True, 0)
+        self.inp.show()
+
+        self.scroll = Gtk.ScrolledWindow()
+        self.inp.pack_start(self.scroll, True, True, 0)
+        self.scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.ALWAYS)
+        self.scroll.show()
+
+        self.text = Gtk.TextView()
+        self.text.modify_font(Pango.FontDescription("monospace 8"))
+        self.text.set_editable(False)
+        self.text.set_wrap_mode(Gtk.WrapMode.WORD)
+        self.text.set_size_request(500, 400)
+        self.scroll.add(self.text)
+        self.text.show()
+
+        buffer = self.text.get_buffer()
+        self.normal = buffer.create_tag("normal")
+        self.title = buffer.create_tag("title")
+        self.title.set_property("weight", 600)
+        self.error = buffer.create_tag("error")
+        self.error.set_property("foreground", "red")
+        self.command = buffer.create_tag("command")
+        self.command.set_property("weight", 600)
+        self.command.set_property("foreground", "blue")
+
+        self.inputbox = Gtk.HBox(spacing=2)
+        self.pack_end(self.inputbox, False, True, 0)
+        self.inputbox.show()
+
+        self.prompt = Gtk.Label(label=sys.ps1)
+        self.prompt.set_padding(2, 0)
+        self.prompt.set_size_request(26, -1)
+        self.inputbox.pack_start(self.prompt, False, False, 0)
+        self.prompt.show()
+
+        self.line = Gtk.Entry()
+        self.line.set_size_request(400, -1)
+        self.line.connect("key_press_event", self.key_function)
+        self.inputbox.pack_start(self.line, True, True, 2)
+        self.line.show()
+
+        # now let the text box be resized
+        self.text.set_size_request(0, 0)
+        self.line.set_size_request(0, -1)
+
+        self.namespace = namespace
+
+        self.cmd = ""
+        self.cmd2 = ""
+
+        # set up hooks for standard output.
+        self.stdout = gtkoutfile(self, sys.stdout.fileno(), self.normal)
+        try:
+            # this will mostly fail on win32 ...
+            self.stderr = gtkoutfile(self, sys.stderr.fileno(), self.error)
+        except:
+            # ... but gtkoutfile is not using the fileno anyway
+            self.stderr = gtkoutfile(self, -1, self.error)
+        # set up command history
+        self.history = [""]
+        self.histpos = 0
+        self.namespace["__history__"] = self.history
+
+    def scroll_to_end(self):
+        iter = self.text.get_buffer().get_end_iter()
+        self.text.scroll_to_iter(iter, 0, False, 0, 0)
+        return False  # don't requeue this handler
+
+    def text_insert(self, tag, s):
+        buffer = self.text.get_buffer()
+        iter = buffer.get_end_iter()
+        buffer.insert_with_tags(iter, s, tag)
+        GObject.idle_add(self.scroll_to_end)
+
+    def init(self):
+        self.text.realize()
+        # ??? self.text.style = self.text.get_style()
+        # self.text.fg = self.text.style.fg[Gtk.StateType.NORMAL]
+        # self.text.bg = self.text.style.white
+
+        self.text_insert(
+            self.title,
+            "Python %s\n%s\n\n" % (sys.version, sys.copyright)
+            + "Interactive Python-GTK Console - "
+            + "Copyright (C) 1998 James Henstridge\n\n"
+            + self.copyright
+            + "\n",
+        )
+        self.line.grab_focus()
+
+    def quit(self, *args):
+        self.destroy()
+        if self.quit_cb:
+            self.quit_cb()
+
+    def key_function(self, entry, event):
+        if event.keyval == Gdk.KEY_Return:
+            self.line.emit_stop_by_name("key_press_event")
+            self.eval()
+        if event.keyval == Gdk.KEY_Tab:
+            self.line.emit_stop_by_name("key_press_event")
+            self.line.append_text("\t")
+            GObject.idle_add(self.focus_text)
+        elif event.keyval in (Gdk.KEY_KP_Up, Gdk.KEY_Up):
+            self.line.emit_stop_by_name("key_press_event")
+            self.historyUp()
+            GObject.idle_add(self.focus_text)
+        elif event.keyval in (Gdk.KEY_KP_Down, Gdk.KEY_Down):
+            self.line.emit_stop_by_name("key_press_event")
+            self.historyDown()
+            GObject.idle_add(self.focus_text)
+        elif (
+            event.keyval in (Gdk.KEY_D, Gdk.KEY_d)
+            and event.get_state() & Gdk.ModifierType.CONTROL_MASK
+        ):
+            self.line.emit_stop_by_name("key_press_event")
+            self.ctrld()
+
+    def focus_text(self):
+        self.line.grab_focus()
+        return False  # don't requeue this handler
+
+    def ctrld(self):
+        self.quit()
+        pass
+
+    def historyUp(self):
+        if self.histpos > 0:
+            l = self.line.get_text()
+            if len(l) > 0 and l[0] == "\n":
+                l = l[1:]
+            if len(l) > 0 and l[-1] == "\n":
+                l = l[:-1]
+            self.history[self.histpos] = l
+            self.histpos = self.histpos - 1
+            self.line.set_text(self.history[self.histpos])
+
+    def historyDown(self):
+        if self.histpos < len(self.history) - 1:
+            l = self.line.get_text()
+            if len(l) > 0 and l[0] == "\n":
+                l = l[1:]
+            if len(l) > 0 and l[-1] == "\n":
+                l = l[:-1]
+            self.history[self.histpos] = l
+            self.histpos = self.histpos + 1
+            self.line.set_text(self.history[self.histpos])
+
+    def eval(self):
+        l = self.line.get_text() + "\n"
+        if len(l) > 1 and l[0] == "\n":
+            l = l[1:]
+        self.histpos = len(self.history) - 1
+        if len(l) > 0 and l[-1] == "\n":
+            self.history[self.histpos] = l[:-1]
+        else:
+            self.history[self.histpos] = l
+        self.line.set_text("")
+        self.text_insert(self.command, self.prompt.get_label() + l)
+        if l == "\n":
+            self.run(self.cmd)
+            self.cmd = ""
+            self.cmd2 = ""
+            return
+        self.histpos = self.histpos + 1
+        self.history.append("")
+        self.cmd = self.cmd + l
+        self.cmd2 = self.cmd2 + remQuotStr(l)
+        l = l.rstrip()
+        if (
+            not bracketsBalanced(self.cmd2)
+            or l[-1] == ":"
+            or l[-1] == "\\"
+            or l[0] in " \11"
+        ):
+            self.prompt.set_text(sys.ps2)
+            self.prompt.queue_draw()
+            return
+        self.run(self.cmd)
+        self.cmd = ""
+        self.cmd2 = ""
+
+    def run(self, cmd):
+        sys.stdout, self.stdout = self.stdout, sys.stdout
+        sys.stderr, self.stderr = self.stderr, sys.stderr
+        try:
+            try:
+                r = eval(cmd, self.namespace, self.namespace)
+                if r is not None:
+                    print(repr(r))
+            except SyntaxError:
+                exec(cmd, self.namespace)
+        except:
+            if hasattr(sys, "last_type") and sys.last_type == SystemExit:
+                self.quit()
+            else:
+                traceback.print_exc()
+        self.prompt.set_text(sys.ps1)
+        self.prompt.queue_draw()
+        # adj = self.text.get_vadjustment()
+        # adj.set_value(adj.upper - adj.page_size)
+        sys.stdout, self.stdout = self.stdout, sys.stdout
+        sys.stderr, self.stderr = self.stderr, sys.stderr
+
+
+def gtk_console(ns, title="Python", copyright="", menu=None):
+    win = Gtk.Window()
+    win.set_default_size(475, 300)
+    # win.connect("destroy", mainquit)
+    win.set_title(title)
+
+    def quit(win=win):
+        win.destroy()
+
+    cons = Console(namespace=ns, copyright=copyright, quit_cb=quit)
+    if menu:
+        box = Gtk.VBox()
+        win.add(box)
+        box.show()
+        box.pack_start(menu, False, True, 0)
+        menu.show()
+        box.pack_start(cons, True, True, 0)
+    else:
+        win.add(cons)
+    cons.show()
+    win.show()
+    cons.init()
+
 
 # set up as a dia plugin
-try :
-       import dia
-       def open_console(data, flags):
-               gtk_console({'__builtins__': __builtins__, '__name__': '__main__',
-                            '__doc__': None, 'dia': dia}, _('Python Dia Console'))
-
-       dia.register_action ("DialogsPythonconsole", _("_Python Console"),
-                             "/DisplayMenu/Dialogs/DialogsExtensionStart",
-                              open_console)
-
-except :
-       print 'Failed to import Dia ...'
-       gtk_console({'__builtins__': __builtins__, '__name__': '__main__',
-               '__doc__': None})
-       gtk.main()
+try:
+    import dia
+
+    def open_console(data, flags):
+        gtk_console(
+            {
+                "__builtins__": __builtins__,
+                "__name__": "__main__",
+                "__doc__": None,
+                "dia": dia,
+            },
+            _("Python Dia Console"),
+        )
+
+    dia.register_action(
+        "DialogsPythonconsole",
+        _("_Python Console"),
+        "/DisplayMenu/Dialogs/DialogsExtensionStart",
+        open_console,
+    )
+
+except:
+    print("Failed to import Dia ...")
+    gtk_console({"__builtins__": __builtins__, "__name__": "__main__", "__doc__": None})
+    Gtk.main()
diff --git a/plug-ins/python/imgmap.py b/plug-ins/python/imgmap.py
index 316b7e973..6f957a119 100644
--- a/plug-ins/python/imgmap.py
+++ b/plug-ins/python/imgmap.py
@@ -15,7 +15,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import dia, sys, os.path, string
+import dia, sys, os.path
 
 import gettext
 _ = gettext.gettext
@@ -38,7 +38,7 @@ class ObjRenderer :
                width = int((r.right - r.left) * scale)
                height = int((r.bottom - r.top) * scale)
                name = os.path.split (filename)[1]
-               fname = name[:string.find(name, ".")] + ".png" # guessing
+               fname = name[:name.find(".")] + ".png" # guessing
                self.f.write ('<image src="%s" width="%d", height="%d" usemap="#%s">\n' % (fname, width, 
height, name))
                self.f.write ('<map name="%s">\n' % (name,))
 
@@ -54,17 +54,17 @@ class ObjRenderer :
        def WriteAreas (self, layer, scale) :
                for o in layer.objects :
                        r = o.bounding_box
-                       if o.properties.has_key ("name") :
+                       if "name" in o.properties :
                                url = o.properties["name"].value
-                       elif o.properties.has_key ("text") :
+                       elif "text" in o.properties :
                                url = o.properties["text"].value.text
                        else :
                                continue
-                       if len(url) == 0 or string.find (url, " ") >= 0 :
+                       if len(url) == 0 or url.find (" ") >= 0 :
                                continue
 
                        alt = url
-                       if o.properties.has_key ("comment") :
+                       if "comment" in o.properties :
                                alt = o.properties["comment"].value
                        # need to sale the original coords to the bitmap size
                        x1 = int(r.left * scale) + self.xofs
diff --git a/plug-ins/python/meson.build b/plug-ins/python/meson.build
index 92488a619..134b3f0e9 100644
--- a/plug-ins/python/meson.build
+++ b/plug-ins/python/meson.build
@@ -1,7 +1,4 @@
-pymod = import('python')
-
-py_inst = pymod.find_installation('python2', required: false)
-py_dep = py_inst.dependency(version: '>= 2.3')
+py3_dep = dependency('python3-embed', version: '>= 3.7')
 
 sources = files(
     'pydia-color.c',
@@ -36,7 +33,7 @@ python_scripts = [
     'diasvg_import.py',
     'dot.py',
     'doxrev.py',
-    'gtkcons.py',  # Will only run if pygtk is present.
+    'gtkcons.py',
     'imgmap.py',
     'otypes.py',
     'pydiadoc.py',
@@ -45,12 +42,12 @@ python_scripts = [
     'select_empty.py',
 ]
 
-if py_dep.found()
+if py3_dep.found()
     # Same as layout plugin.
     shared_module(
         'python_plugin',
         sources + [config_h],
-        dependencies: [libc_dep, libxml_dep, libgtk_dep, py_dep, libdia_dep],
+        dependencies: [libc_dep, libxml_dep, libgtk_dep, py3_dep, libdia_dep],
         include_directories: [configuration_inc, diaapp_inc],
         link_with: [diaapp], # Naughty
         install: true,
diff --git a/plug-ins/python/otypes.py b/plug-ins/python/otypes.py
index a3db90da2..0d14ff251 100644
--- a/plug-ins/python/otypes.py
+++ b/plug-ins/python/otypes.py
@@ -19,7 +19,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import sys, dia, string
+import sys, dia
 
 import gettext
 _ = gettext.gettext
@@ -43,7 +43,7 @@ def otypes_cb(data, flags) :
        layer = data.active_layer
 
        otypes = dia.registered_types()
-       keys = otypes.keys()
+       keys = list(otypes.keys())
        keys.sort()
 
        # property keys w/o overlap
@@ -57,8 +57,8 @@ def otypes_cb(data, flags) :
        text_props = ["text_colour", "text_font", "text_height", "text"] # "text_alignment", "text_pos"
 
        packages = {}
-       for s in keys :
-               kt = string.split(s, " - ")
+       for s in keys:
+               kt = s.split(" - ")
                if len(kt) == 2 :
                        if len(kt[0]) == 0 :
                                sp = "<unnamed>"
@@ -68,7 +68,7 @@ def otypes_cb(data, flags) :
                else :
                        sp = "<broken>"
                        st = kt[0]
-               if packages.has_key(sp) :
+               if sp in packages:
                        packages[sp].append(st)
                else :
                        packages[sp] = [st]
@@ -79,7 +79,7 @@ def otypes_cb(data, flags) :
        maxy = 0
        maxx = 0
 
-       for sp in packages.keys() :
+       for sp in list(packages.keys()):
                pkg = packages[sp]
                op, h1, h2 = dtp.create(0.0, cy + 1.0)
                op.properties["name"] = sp
@@ -99,16 +99,16 @@ def otypes_cb(data, flags) :
                        n_line = 0
                        n_fill = 0
                        n_text = 0
-                       if otypes.has_key(st) :
+                       if st in otypes:
                                o_real, h5, h6 = dia.get_object_type(st).create(0,0)
-                       elif otypes.has_key(sp + " - " + st) :
+                       elif sp + " - " + st in otypes:
                                o_real, h5, h6 = dia.get_object_type(sp + " - " + st).create(0,0)
                        else :
                                o_real = None
-                               print "Failed to create object", sp, st
+                               print("Failed to create object", sp, st)
                        formal_params = []
                        if not o_real is None :
-                               for p in o_real.properties.keys() :
+                               for p in list(o_real.properties.keys()):
                                        if p in object_props : n_object = n_object + 1
                                        elif p in orthconn_props : n_orthconn = n_orthconn + 1
                                        elif p in element_props : n_element = n_element + 1
@@ -122,19 +122,19 @@ def otypes_cb(data, flags) :
                                        formal_params.append(('Line', ''))
                                else : # need to add the incomplete set
                                        for pp in line_props :
-                                               if o_real.properties.has_key(pp) :
+                                               if pp in o_real.properties:
                                                        attrs.append((pp, o_real.properties[pp].type, '', '', 
0, 0, 0))
                                if n_fill == len(fill_props) :
                                        formal_params.append(('Fill', ''))
                                else :
                                        for pp in fill_props :
-                                               if o_real.properties.has_key(pp) :
+                                               if pp in o_real.properties:
                                                        attrs.append((pp, o_real.properties[pp].type, '', '', 
0, 0, 0))
                                if n_text == len(text_props) :
                                        formal_params.append(('Text', ''))
                                else :
                                        for pp in text_props :
-                                               if o_real.properties.has_key(pp) :
+                                               if pp in o_real.properties:
                                                        attrs.append((pp, o_real.properties[pp].type, '', '', 
0, 0, 0))
                        if n_orthconn == len(orthconn_props) :
                                oc.properties["stereotype"] = "OrthConn"
@@ -148,7 +148,7 @@ def otypes_cb(data, flags) :
                        elif n_object == len(object_props) :
                                oc.properties["stereotype"] = "Object"
                        else :
-                               print "Huh?", st
+                               print("Huh?", st)
                                oc.properties["fill_colour"] = "red"
                        oc.properties["attributes"] = attrs
                        if len(formal_params) > 0 :
diff --git a/plug-ins/python/pydia-color.c b/plug-ins/python/pydia-color.c
index 7f101550f..9b048ef9c 100644
--- a/plug-ins/python/pydia-color.c
+++ b/plug-ins/python/pydia-color.c
@@ -52,23 +52,63 @@ PyDiaColor_Dealloc (PyObject *self)
   PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaColor_Compare(PyDiaColor *self,
-                  PyDiaColor *other)
+static PyObject *
+PyDiaColor_RichCompare (PyObject *self, PyObject *other, int op)
 {
-  return memcmp(&(self->color), &(other->color), sizeof(Color));
+  int res = memcmp (&(((PyDiaColor *) self)->color),
+                    &(((PyDiaColor *) other)->color),
+                    sizeof (Color));
+
+  switch (op) {
+    case Py_LT:
+      if (res < 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_LE:
+      if (res <= 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_NE:
+      if (res != 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_GT:
+      if (res > 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_GE:
+      if (res >= 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_EQ:
+      if (res == 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    default:
+      Py_RETURN_NOTIMPLEMENTED;
+  }
+
+  Py_RETURN_FALSE;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaColor_Hash(PyObject *self)
+PyDiaColor_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
 
@@ -76,16 +116,18 @@ PyDiaColor_Hash(PyObject *self)
  * Repr / _Str
  */
 static PyObject *
-PyDiaColor_Str (PyDiaColor *self)
+PyDiaColor_Str (PyObject *self)
 {
-  PyObject* py_s;
-  gchar* s = g_strdup_printf ("(%f,%f,%f,%f)",
-                              (float) (self->color.red),
-                              (float) (self->color.green),
-                              (float) (self->color.blue),
-                              (float) (self->color.alpha));
-  py_s = PyString_FromString (s);
+  PyObject *py_s;
+  char *s = g_strdup_printf ("(%f,%f,%f,%f)",
+                             (float) (((PyDiaColor *) self)->color.red),
+                             (float) (((PyDiaColor *) self)->color.green),
+                             (float) (((PyDiaColor *) self)->color.blue),
+                             (float) (((PyDiaColor *) self)->color.alpha));
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
@@ -101,40 +143,22 @@ static PyMemberDef PyDiaColor_Members[] = {
       "double: alpha color component [0 .. 1.0]" },
     { NULL }
 };
+
+
 /*
- * Python objetcs
+ * Python objects
  */
 PyTypeObject PyDiaColor_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "DiaColor",
-    sizeof(PyDiaColor),
-    0,
-    (destructor)PyDiaColor_Dealloc,
-    (printfunc)0,
-    (getattrfunc)0,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaColor_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaColor_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaColor_Str,
-    PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "A color either defined by a color string or by a tuple with three elements "
-    "(r, g, b) with type float 0.0 ... 1.0 or range int 0 ... 65535",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaColor_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "DiaColor",
+  .tp_basicsize = sizeof (PyDiaColor),
+  .tp_dealloc = PyDiaColor_Dealloc,
+  .tp_richcompare = PyDiaColor_RichCompare,
+  .tp_hash = PyDiaColor_Hash,
+  .tp_str = PyDiaColor_Str,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_doc = "A color either defined by a color string or by a tuple with "
+            "three elements (r, g, b) with type float 0.0 ... 1.0 or range "
+            "int 0 ... 65535",
+  .tp_members = PyDiaColor_Members,
 };
diff --git a/plug-ins/python/pydia-cpoint.c b/plug-ins/python/pydia-cpoint.c
index 61f207760..15ffde7a5 100644
--- a/plug-ins/python/pydia-cpoint.c
+++ b/plug-ins/python/pydia-cpoint.c
@@ -25,67 +25,89 @@
 
 #include <structmember.h> /* PyMemberDef */
 
+
 PyObject *
-PyDiaConnectionPoint_New(ConnectionPoint *cpoint)
+PyDiaConnectionPoint_New (ConnectionPoint *cpoint)
 {
-    PyDiaConnectionPoint *self;
+  PyDiaConnectionPoint *self;
+
+  self = PyObject_NEW (PyDiaConnectionPoint, &PyDiaConnectionPoint_Type);
+
+  if (!self) {
+    return NULL;
+  }
 
-    self = PyObject_NEW(PyDiaConnectionPoint, &PyDiaConnectionPoint_Type);
+  self->cpoint = cpoint;
 
-    if (!self) return NULL;
-    self->cpoint = cpoint;
-    return (PyObject *)self;
+  return (PyObject *) self;
 }
 
+
 static void
-PyDiaConnectionPoint_Dealloc(PyDiaConnectionPoint *self)
+PyDiaConnectionPoint_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaConnectionPoint_Compare(PyDiaConnectionPoint *self,
-                            PyDiaConnectionPoint *other)
+
+static PyObject *
+PyDiaConnectionPoint_RichCompare (PyObject *self,
+                                  PyObject *other,
+                                  int       op)
 {
-    if (self->cpoint == other->cpoint) return 0;
-    if (self->cpoint > other->cpoint) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaConnectionPoint *) self)->cpoint,
+                         ((PyDiaConnectionPoint *) other)->cpoint,
+                         op);
 }
 
+
 static long
-PyDiaConnectionPoint_Hash(PyDiaConnectionPoint *self)
+PyDiaConnectionPoint_Hash (PyObject *self)
 {
-    return (long)self->cpoint;
+  return (long) ((PyDiaConnectionPoint *) self)->cpoint;
 }
 
+
 static PyObject *
-PyDiaConnectionPoint_GetAttr(PyDiaConnectionPoint *self, gchar *attr)
+PyDiaConnectionPoint_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[sssss]", "connected", "object", "pos", "flags", "directions");
-    else if (!strcmp(attr, "pos"))
-       return PyDiaPoint_New(&(self->cpoint->pos));
-    else if (!strcmp(attr, "object"))
-       return PyDiaObject_New(self->cpoint->object);
-    else if (!strcmp(attr, "flags"))
-       return PyInt_FromLong(self->cpoint->flags);
-    else if (!strcmp(attr, "directions"))
-       return PyInt_FromLong(self->cpoint->directions);
-    else if (!strcmp(attr, "connected")) {
-       PyObject *ret;
-       GList *tmp;
-       gint i;
-
-       ret = PyTuple_New(g_list_length(self->cpoint->connected));
-       for (i = 0, tmp = self->cpoint->connected; tmp; i++, tmp = tmp->next)
-           PyTuple_SetItem(ret, i, PyDiaObject_New((DiaObject *)tmp->data));
-       return ret;
+  PyDiaConnectionPoint *self = (PyDiaConnectionPoint *) obj;
+
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue("[sssss]", "connected", "object", "pos", "flags", "directions");
+  } else if (!g_strcmp0 (attr, "pos")) {
+    return PyDiaPoint_New (&(self->cpoint->pos));
+  } else if (!g_strcmp0 (attr, "object")) {
+    return PyDiaObject_New (self->cpoint->object);
+  } else if (!g_strcmp0 (attr, "flags")) {
+    return PyLong_FromLong (self->cpoint->flags);
+  } else if (!g_strcmp0 (attr, "directions")) {
+    return PyLong_FromLong (self->cpoint->directions);
+  } else if (!g_strcmp0 (attr, "connected")) {
+    PyObject *ret;
+    GList *tmp;
+    int i;
+
+    ret = PyTuple_New (g_list_length (self->cpoint->connected));
+    for (i = 0, tmp = self->cpoint->connected; tmp; i++, tmp = tmp->next) {
+      PyTuple_SetItem (ret, i, PyDiaObject_New (DIA_OBJECT (tmp->data)));
     }
+    return ret;
+  }
 
-    PyErr_SetString(PyExc_AttributeError, attr);
-    return NULL;
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 #define T_INVALID -1 /* can't allow direct access due to pyobject->cpoint indirection */
 static PyMemberDef PyDiaConnectionPoint_Members[] = {
     { "connected", T_INVALID, 0, RESTRICTED|READONLY,
@@ -101,37 +123,16 @@ static PyMemberDef PyDiaConnectionPoint_Members[] = {
     { NULL }
 };
 
+
 PyTypeObject PyDiaConnectionPoint_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.ConnectionPoint",
-    sizeof(PyDiaConnectionPoint),
-    0,
-    (destructor)PyDiaConnectionPoint_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaConnectionPoint_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaConnectionPoint_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaConnectionPoint_Hash,
-    (ternaryfunc)0,
-    (reprfunc)0,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "One of the major features of Dia are connectable objects. They work by this type accesible "
-    "through dia.Object.connections[].",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaConnectionPoint_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.ConnectionPoint",
+  .tp_basicsize = sizeof (PyDiaConnectionPoint),
+  .tp_dealloc = PyDiaConnectionPoint_Dealloc,
+  .tp_getattro = PyDiaConnectionPoint_GetAttr,
+  .tp_richcompare = PyDiaConnectionPoint_RichCompare,
+  .tp_hash = PyDiaConnectionPoint_Hash,
+  .tp_doc = "One of the major features of Dia are connectable objects. They "
+            "work by this type accesible through dia.Object.connections[].",
+  .tp_members = PyDiaConnectionPoint_Members,
 };
diff --git a/plug-ins/python/pydia-diagram.c b/plug-ins/python/pydia-diagram.c
index 49c9125ed..158a5ebe7 100644
--- a/plug-ins/python/pydia-diagram.c
+++ b/plug-ins/python/pydia-diagram.c
@@ -62,7 +62,7 @@ PyDiaDiagram_Dealloc (PyDiaDiagram *self)
 static PyObject *
 PyDiaDiagram_Str (PyDiaDiagram *self)
 {
-  return PyString_FromString (PYDIA_DIAGRAM (self)->filename);
+  return PyUnicode_FromString (PYDIA_DIAGRAM (self)->filename);
 }
 
 
@@ -381,7 +381,7 @@ PyDiaDiagram_Save (PyDiaDiagram *self, PyObject *args)
   dia_context_reset (ctx);
   dia_context_release (ctx);
 
-  return PyInt_FromLong (ret);
+  return PyLong_FromLong (ret);
 }
 
 
@@ -624,7 +624,7 @@ PyDiaDiagram_GetDisplays (PyDiaDiagram *self, void *closure)
 static PyObject *
 PyDiaDiagram_GetFilename (PyDiaDiagram *self, void *closure)
 {
-  return PyString_FromString (PYDIA_DIAGRAM (self)->filename);
+  return PyUnicode_FromString (PYDIA_DIAGRAM (self)->filename);
 }
 
 
@@ -675,7 +675,7 @@ static PyGetSetDef PyDiaDiagram_GetSetters[] = {
 
 
 PyTypeObject PyDiaDiagram_Type = {
-  PyObject_HEAD_INIT (NULL)
+  PyVarObject_HEAD_INIT (NULL, 0)
   .tp_name = "dia.Diagram",
   .tp_basicsize = sizeof (PyDiaDiagram),
   .tp_flags = Py_TPFLAGS_DEFAULT,
diff --git a/plug-ins/python/pydia-diagramdata.c b/plug-ins/python/pydia-diagramdata.c
index 37c968f82..ed6c816e4 100644
--- a/plug-ins/python/pydia-diagramdata.c
+++ b/plug-ins/python/pydia-diagramdata.c
@@ -55,37 +55,39 @@ PyDiaDiagramData_New (DiagramData *dd)
 
 
 static void
-PyDiaDiagramData_Dealloc (PyDiaDiagramData *self)
+PyDiaDiagramData_Dealloc (PyObject *self)
 {
-  g_clear_object (&self->data);
+  g_clear_object (&((PyDiaDiagramData *) self)->data);
 
   PyObject_DEL (self);
 }
 
 
-static int
-PyDiaDiagramData_Compare (PyDiaDiagramData *self, PyDiaDiagramData *other)
+static PyObject *
+PyDiaDiagramData_RichCompare (PyObject *self,
+                              PyObject *other,
+                              int       op)
 {
-  if (self->data == other->data) return 0;
-  if (self->data > other->data) return -1;
-  return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaDiagramData *) self)->data,
+                         ((PyDiaDiagramData *) other)->data,
+                         op);
 }
 
 
 static long
-PyDiaDiagramData_Hash (PyDiaDiagramData *self)
+PyDiaDiagramData_Hash (PyObject *self)
 {
-  return (long) self->data;
+  return (long) ((PyDiaDiagramData *) self)->data;
 }
 
 
 static PyObject *
-PyDiaDiagramData_Str (PyDiaDiagramData *self)
+PyDiaDiagramData_Str (PyObject *self)
 {
   PyObject *py_s;
   char *s = g_strdup_printf ("<PyDiaDiagramData %p>", self);
 
-  py_s = PyString_FromString (s);
+  py_s = PyUnicode_FromString (s);
 
   g_clear_pointer (&s, g_free);
 
@@ -347,7 +349,7 @@ static PyMethodDef PyDiaDiagramData_Methods[] = {
 static PyObject *
 PyDiaDiagramData_GetExtents (PyDiaDiagramData *self, void *closure)
 {
-  return PyDiaRectangle_New (&self->data->extents, NULL);
+  return PyDiaRectangle_New (&self->data->extents);
 }
 
 
@@ -461,14 +463,14 @@ static PyGetSetDef PyDiaDiagramData_GetSetters[] = {
 
 
 PyTypeObject PyDiaDiagramData_Type = {
-  PyObject_HEAD_INIT (NULL)
+  PyVarObject_HEAD_INIT (NULL, 0)
   .tp_name = "dia.DiagramData",
   .tp_basicsize = sizeof (PyDiaDiagramData),
   .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-  .tp_dealloc = (destructor) PyDiaDiagramData_Dealloc,
-  .tp_compare = (cmpfunc) PyDiaDiagramData_Compare,
-  .tp_hash = (hashfunc) PyDiaDiagramData_Hash,
-  .tp_str = (reprfunc) PyDiaDiagramData_Str,
+  .tp_dealloc = PyDiaDiagramData_Dealloc,
+  .tp_richcompare = PyDiaDiagramData_RichCompare,
+  .tp_hash = PyDiaDiagramData_Hash,
+  .tp_str = PyDiaDiagramData_Str,
   .tp_doc = "The 'low level' diagram object. It contains everything to "
             "manipulate diagrams from im- and export filters as well as"
             " from the UI. It does not provide any access to GUI elements "
diff --git a/plug-ins/python/pydia-display.c b/plug-ins/python/pydia-display.c
index e15722310..a2bb2e0a3 100644
--- a/plug-ins/python/pydia-display.c
+++ b/plug-ins/python/pydia-display.c
@@ -37,32 +37,35 @@ PyDiaDisplay_New(DDisplay *disp)
     return (PyObject *)self;
 }
 
+
 static void
-PyDiaDisplay_Dealloc(PyDiaDisplay *self)
+PyDiaDisplay_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaDisplay_Compare(PyDiaDisplay *self, PyDiaDisplay *other)
+
+static PyObject *
+PyDiaDisplay_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->disp == other->disp) return 0;
-    if (self->disp > other->disp) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (self, other, op);
 }
 
+
 static long
-PyDiaDisplay_Hash(PyDiaDisplay *self)
+PyDiaDisplay_Hash (PyObject *self)
 {
-    return (long)self->disp;
+  return (long) ((PyDiaDisplay *) self)->disp;
 }
 
+
 static PyObject *
-PyDiaDisplay_Str(PyDiaDisplay *self)
+PyDiaDisplay_Str (PyObject *self)
 {
-    return PyString_FromString(self->disp->diagram->filename);
+  return PyUnicode_FromString (((PyDiaDisplay *) self)->disp->diagram->filename);
 }
 
+
 /* methods here */
 
 static PyObject *
@@ -239,58 +242,59 @@ static PyMemberDef PyDiaDisplay_Members[] = {
     { NULL }
 };
 
+
 static PyObject *
-PyDiaDisplay_GetAttr(PyDiaDisplay *self, gchar *attr)
+PyDiaDisplay_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[ssss]", "diagram", "origin", "visible",
-                            "zoom_factor");
-    else if (!strcmp(attr, "diagram"))
-       return PyDiaDiagram_New(self->disp->diagram);
+  PyDiaDisplay *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaDisplay *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ssss]",
+                          "diagram", "origin", "visible", "zoom_factor");
+  } else if (!g_strcmp0 (attr, "diagram")) {
+    return PyDiaDiagram_New (self->disp->diagram);
     /* FIXME: shouldn't it have only one name */
-    else if (!strcmp(attr, "origo") || !strcmp(attr, "origion") || !strcmp(attr, "origin"))
-       return Py_BuildValue("(dd)", self->disp->origo.x, self->disp->origo.y);
-    else if (!strcmp(attr, "zoom_factor"))
-       return PyFloat_FromDouble(self->disp->zoom_factor);
-    else if (!strcmp(attr, "visible"))
-       return Py_BuildValue("(dddd)", self->disp->visible.top,
-                            self->disp->visible.left,
-                            self->disp->visible.bottom,
-                            self->disp->visible.right);
-
-    return Py_FindMethod(PyDiaDisplay_Methods, (PyObject *)self, attr);
+  } else if (!g_strcmp0 (attr, "origo") ||
+             !g_strcmp0 (attr, "origion") ||
+             !g_strcmp0 (attr, "origin")) {
+    return Py_BuildValue ("(dd)",
+                          self->disp->origo.x,
+                          self->disp->origo.y);
+  } else if (!g_strcmp0 (attr, "zoom_factor")) {
+    return PyFloat_FromDouble (self->disp->zoom_factor);
+  } else if (!g_strcmp0 (attr, "visible")) {
+    return Py_BuildValue ("(dddd)",
+                          self->disp->visible.top,
+                          self->disp->visible.left,
+                          self->disp->visible.bottom,
+                          self->disp->visible.right);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 PyTypeObject PyDiaDisplay_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Display",
-    sizeof(PyDiaDisplay),
-    0,
-    (destructor)PyDiaDisplay_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaDisplay_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaDisplay_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaDisplay_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaDisplay_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "A Diagram can have multiple displays but every Display has just one Diagram.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaDisplay_Methods, /* tp_methods */
-    PyDiaDisplay_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Display",
+  .tp_basicsize = sizeof (PyDiaDisplay),
+  .tp_dealloc = PyDiaDisplay_Dealloc,
+  .tp_getattro = PyDiaDisplay_GetAttr,
+  .tp_richcompare = PyDiaDisplay_RichCompare,
+  .tp_hash = PyDiaDisplay_Hash,
+  .tp_str = PyDiaDisplay_Str,
+  .tp_doc = "A Diagram can have multiple displays but every Display has just "
+            "one Diagram.",
+  .tp_methods = PyDiaDisplay_Methods,
+  .tp_members = PyDiaDisplay_Members,
 };
diff --git a/plug-ins/python/pydia-error.c b/plug-ins/python/pydia-error.c
index 2386e97b5..c8d5d8c2f 100644
--- a/plug-ins/python/pydia-error.c
+++ b/plug-ins/python/pydia-error.c
@@ -76,119 +76,119 @@ PyObject* PyDiaError_New (const char* s, gboolean unbuffered)
   return (PyObject *)self;
 }
 
+
 /*
  * Dealloc
  */
 static void
-PyDiaError_Dealloc(PyDiaError *self)
+PyDiaError_Dealloc (PyObject *self)
 {
-  if (self->str)
-    g_string_free (self->str, TRUE);
-  PyObject_DEL(self);
+  if (((PyDiaError *) self)->str) {
+    g_string_free (((PyDiaError *) self)->str, TRUE);
+  }
+
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaError_Compare(PyDiaError *self,
-                  PyDiaError *other)
+static PyObject *
+PyDiaError_RichCompare (PyObject *a,
+                        PyObject *b,
+                        int       op)
 {
-  int len = 0;
+  PyDiaError *self = (PyDiaError *) a;
+  PyDiaError *other = (PyDiaError *) b;
+  PyObject *left_str;
+  PyObject *right_str;
+  PyObject *result;
+
+  if (self->str) {
+    left_str = PyUnicode_FromStringAndSize (self->str->str, self->str->len);
+  } else {
+    left_str = Py_None;
+    Py_INCREF (left_str);
+  }
+
+  if (other->str) {
+    right_str = PyUnicode_FromStringAndSize (other->str->str, other->str->len);
+  } else {
+    right_str = Py_None;
+    Py_INCREF (right_str);
+  }
 
-  if (self->str == other->str) return 0;
-  if (NULL == self->str) return -1;
-  if (NULL == other->str) return -1;
+  result = PyUnicode_RichCompare (left_str, right_str, op);
 
-  len = (self->str->len > other->str->len ? other->str->len : self->str->len);
-  return memcmp(self->str->str, other->str->str, len);
+  Py_DECREF (left_str);
+  Py_DECREF (right_str);
+
+  return result;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaError_Hash(PyObject *self)
+PyDiaError_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
+
 static PyObject *
-PyDiaError_Write(PyDiaError *self, PyObject *args)
+PyDiaError_Write (PyDiaError *self, PyObject *args)
 {
-  PyObject* obj;
-  gchar* s;
+  PyObject *obj;
+  const char *s;
 
-  if (!PyArg_ParseTuple(args, "O", &obj))
+  if (!PyArg_ParseTuple (args, "O", &obj)) {
     return NULL;
+  }
 
-  s = PyString_AsString (obj);
+  s = PyUnicode_AsUTF8 (obj);
 
-  if (self->str)
+  if (self->str) {
     g_string_append (self->str, s);
+  }
 
   g_printerr ("%s", s);
 
-  Py_INCREF(Py_None);
-  return Py_None;
+  Py_RETURN_NONE;
 }
 
+
 static PyMethodDef PyDiaError_Methods [] = {
     { "write", (PyCFunction)PyDiaError_Write, METH_VARARGS },
     { NULL, 0, 0, NULL }
 };
 
-/*
- * GetAttr
- */
-static PyObject *
-PyDiaError_GetAttr(PyDiaError *self, gchar *attr)
-{
-  return Py_FindMethod(PyDiaError_Methods, (PyObject *)self, attr);
-}
 
 /*
  * Repr / _Str
  */
 static PyObject *
-PyDiaError_Str(PyDiaError *self)
+PyDiaError_Str (PyObject *self)
 {
-  return PyString_FromString(self->str->str);
+  return PyUnicode_FromString (((PyDiaError *) self)->str->str);
 }
 
+
 /*
  * Python objetcs
  */
 PyTypeObject PyDiaError_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "DiaError",
-    sizeof(PyDiaError),
-    0,
-    (destructor)PyDiaError_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaError_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaError_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaError_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaError_Str,
-    (getattrofunc)0L,
-    (setattrofunc)0L,
-    (PyBufferProcs *)0L,
-    0L, /* Flags */
-    "The error object is just a helper to redirect errors to messages",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaError_Methods, /* tp_methods */
-    NULL, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "DiaError",
+  .tp_basicsize = sizeof (PyDiaError),
+  .tp_dealloc = PyDiaError_Dealloc,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_richcompare = PyDiaError_RichCompare,
+  .tp_hash = PyDiaError_Hash,
+  .tp_str = PyDiaError_Str,
+  .tp_doc = "The error object is just a helper to redirect errors to "
+            "messages",
+  .tp_methods = PyDiaError_Methods,
 };
diff --git a/plug-ins/python/pydia-export.c b/plug-ins/python/pydia-export.c
index 24b8772ac..1089cc5ff 100644
--- a/plug-ins/python/pydia-export.c
+++ b/plug-ins/python/pydia-export.c
@@ -36,30 +36,34 @@ PyDiaExportFilter_New(DiaExportFilter *filter)
     return (PyObject *)self;
 }
 
+
 static void
-PyDiaExportFilter_Dealloc(PyDiaExportFilter *self)
+PyDiaExportFilter_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaExportFilter_Compare(PyDiaExportFilter *self, PyDiaExportFilter *other)
+
+static PyObject *
+PyDiaExportFilter_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->filter == other->filter) return 0;
-    if (self->filter > other->filter) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaExportFilter *) self)->filter,
+                         ((PyDiaExportFilter *) other)->filter,
+                         op);
 }
 
+
 static long
-PyDiaExportFilter_Hash(PyDiaExportFilter *self)
+PyDiaExportFilter_Hash (PyObject *self)
 {
-    return (long)self->filter;
+  return (long) ((PyDiaExportFilter *) self)->filter;
 }
 
+
 static PyObject *
-PyDiaExportFilter_Str(PyDiaExportFilter *self)
+PyDiaExportFilter_Str (PyObject *self)
 {
-    return PyString_FromString(self->filter->description);
+  return PyUnicode_FromString (((PyDiaExportFilter *) self)->filter->description);
 }
 
 /*
@@ -79,49 +83,44 @@ static PyMemberDef PyDiaExportFilter_Members[] = {
     { NULL }
 };
 
+
 static PyObject *
-PyDiaExportFilter_GetAttr(PyDiaExportFilter *self, gchar *attr)
+PyDiaExportFilter_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[ss]", "name");
-    else if (!strcmp(attr, "name"))
-       return PyString_FromString(self->filter->description);
-    else if (!strcmp(attr, "unique_name"))
-       return PyString_FromString(self->filter->unique_name);
-
-    return Py_FindMethod(PyDiaExportFilter_Methods, (PyObject *)self, attr);
+  PyDiaExportFilter *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaExportFilter *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ss]", "name", "unique_name");
+  } else if (!g_strcmp0 (attr, "name")) {
+    return PyUnicode_FromString (self->filter->description);
+  } else if (!g_strcmp0 (attr, "unique_name")) {
+    return PyUnicode_FromString (self->filter->unique_name);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 PyTypeObject PyDiaExportFilter_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.ExportFilter",
-    sizeof(PyDiaExportFilter),
-    0,
-    (destructor)PyDiaExportFilter_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaExportFilter_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaExportFilter_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaExportFilter_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaExportFilter_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "returned by dia.register_export() but not used otherwise yet.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaExportFilter_Methods, /* tp_methods */
-    PyDiaExportFilter_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.ExportFilter",
+  .tp_basicsize = sizeof (PyDiaExportFilter),
+  .tp_dealloc = PyDiaExportFilter_Dealloc,
+  .tp_getattro = PyDiaExportFilter_GetAttr,
+  .tp_richcompare = PyDiaExportFilter_RichCompare,
+  .tp_hash = PyDiaExportFilter_Hash,
+  .tp_str = PyDiaExportFilter_Str,
+  .tp_doc = "returned by dia.register_export() but not used otherwise yet.",
+  .tp_methods = PyDiaExportFilter_Methods,
+  .tp_members = PyDiaExportFilter_Members,
 };
diff --git a/plug-ins/python/pydia-font.c b/plug-ins/python/pydia-font.c
index f06404902..5255b3cf5 100644
--- a/plug-ins/python/pydia-font.c
+++ b/plug-ins/python/pydia-font.c
@@ -47,9 +47,9 @@ PyObject* PyDiaFont_New (DiaFont* font)
  * Dealloc
  */
 static void
-PyDiaFont_Dealloc (PyDiaFont *self)
+PyDiaFont_Dealloc (PyObject *self)
 {
-  g_clear_object (&self->font);
+  g_clear_object (&((PyDiaFont *) self)->font);
 
   PyObject_DEL (self);
 }
@@ -57,54 +57,104 @@ PyDiaFont_Dealloc (PyDiaFont *self)
 /*
  * Compare
  */
-static int
-PyDiaFont_Compare(PyDiaFont *self,
-                  PyDiaFont *other)
+static PyObject *
+PyDiaFont_RichCompare (PyObject *a,
+                       PyObject *b,
+                       int       op)
 {
-  int ret;
-
-  if (self->font == other->font)
-    return 0;
-  else if (!self->font)
-    return 1;
-  else if (!other->font)
-    return -1;
-
-  ret = strcmp (dia_font_get_family (self->font),
-                dia_font_get_family (other->font));
-  if (ret != 0)
-    return ret;
-
-  ret = dia_font_get_style (self->font) - dia_font_get_style (other->font);
-  return ret > 0 ? 1 : (ret < 0 ? -1 : 0);
+  PyDiaFont *self = (PyDiaFont *) a;
+  PyDiaFont *other = (PyDiaFont *) b;
+
+  /* Hmm, not sure about this at all */
+
+  switch (op) {
+    case Py_EQ:
+      if ((self->font && other->font) &&
+          (g_strcmp0 (dia_font_get_family (self->font),
+                      dia_font_get_family (other->font)) == 0
+          && dia_font_get_style (self->font) == dia_font_get_style (other->font))) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_NE:
+      if ((self->font && other->font) &&
+          (g_strcmp0 (dia_font_get_family (self->font),
+                      dia_font_get_family (other->font)) != 0
+          || dia_font_get_style (self->font) != dia_font_get_style (other->font))) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_LT:
+      if ((self->font && other->font) && g_strcmp0 (dia_font_get_family (self->font),
+                                                    dia_font_get_family (other->font)) < 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_GT:
+      if ((self->font && other->font) && g_strcmp0 (dia_font_get_family (self->font),
+                                                    dia_font_get_family (other->font)) > 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_LE:
+      if ((self->font && other->font) && g_strcmp0 (dia_font_get_family (self->font),
+                                                    dia_font_get_family (other->font)) <= 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    case Py_GE:
+      if ((self->font && other->font) && g_strcmp0 (dia_font_get_family (self->font),
+                                                    dia_font_get_family (other->font)) >= 0) {
+        Py_RETURN_TRUE;
+      }
+      break;
+    default:
+      Py_RETURN_NOTIMPLEMENTED;
+  }
+
+  Py_RETURN_FALSE;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaFont_Hash(PyObject *self)
+PyDiaFont_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
+
 /*
  * GetAttr
  */
 static PyObject *
-PyDiaFont_GetAttr(PyDiaFont *self, gchar *attr)
+PyDiaFont_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[sss]", "family", "name", "style");
-  else if (!strcmp(attr, "name"))
-    return PyString_FromString(dia_font_get_legacy_name (self->font));
-  else if (!strcmp(attr, "family"))
-    return PyString_FromString(dia_font_get_family (self->font));
-  else if (!strcmp(attr, "style"))
-    return PyInt_FromLong (dia_font_get_style (self->font));
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  PyDiaFont *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaFont *) obj;
+
+  if (!strcmp (attr, "__members__")) {
+    return Py_BuildValue ("[sss]", "family", "name", "style");
+  } else if (!strcmp (attr, "name")) {
+    return PyUnicode_FromString (dia_font_get_legacy_name (self->font));
+  } else if (!strcmp (attr, "family")) {
+    return PyUnicode_FromString (dia_font_get_family (self->font));
+  } else if (!strcmp (attr, "style")) {
+    return PyLong_FromLong (dia_font_get_style (self->font));
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
@@ -112,16 +162,24 @@ PyDiaFont_GetAttr(PyDiaFont *self, gchar *attr)
  * Repr / _Str
  */
 static PyObject *
-PyDiaFont_Str (PyDiaFont *self)
+PyDiaFont_Str (PyObject *obj)
 {
+  PyDiaFont *self = (PyDiaFont *) obj;
   PyObject *ret;
-  char *s = self->font ? g_strdup_printf ("%s %s %s",
-                                          dia_font_get_family (self->font),
-                                          dia_font_get_weight_string (self->font),
-                                          dia_font_get_slant_string (self->font)) : g_strdup ("<DiaFont 
NULL>");
+  char *s;
 
-  ret = PyString_FromString (s);
+  if (self->font) {
+    s = g_strdup_printf ("%s %s %s",
+                         dia_font_get_family (self->font),
+                         dia_font_get_weight_string (self->font),
+                         dia_font_get_slant_string (self->font));
+  } else {
+    s = g_strdup ("<DiaFont NULL>");
+  }
+
+  ret = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return ret;
 }
 
@@ -136,39 +194,20 @@ static PyMemberDef PyDiaFont_Members[] = {
       "int: style flags" },
     { NULL }
 };
+
+
 /*
- * Python objetc
+ * Python object
  */
 PyTypeObject PyDiaFont_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Font",
-    sizeof(PyDiaFont),
-    0,
-    (destructor)PyDiaFont_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaFont_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaFont_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaFont_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaFont_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "Provides access to some objects font property.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaFont_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Font",
+  .tp_basicsize = sizeof (PyDiaFont),
+  .tp_dealloc = PyDiaFont_Dealloc,
+  .tp_getattro = PyDiaFont_GetAttr,
+  .tp_richcompare = PyDiaFont_RichCompare,
+  .tp_hash = PyDiaFont_Hash,
+  .tp_str = PyDiaFont_Str,
+  .tp_doc = "Provides access to some objects font property.",
+  .tp_members = PyDiaFont_Members,
 };
diff --git a/plug-ins/python/pydia-geometry.c b/plug-ins/python/pydia-geometry.c
index a314cde93..8b44d0f64 100644
--- a/plug-ins/python/pydia-geometry.c
+++ b/plug-ins/python/pydia-geometry.c
@@ -24,7 +24,7 @@
 
 #include <structmember.h> /* PyMemberDef */
 
-/* Implements wrappers for Point, DiaRectangle, IntRectangle, BezPoint */
+/* Implements wrappers for Point, DiaRectangle, BezPoint */
 
 /*
  * New
@@ -56,38 +56,42 @@ PyDiaPointTuple_New (Point* pts, int num)
   return ret;
 }
 
+
 /* one of the parameters needs to be NULL, the other is created */
-PyObject*
-PyDiaRectangle_New (DiaRectangle* r, IntRectangle* ri)
+PyObject *
+PyDiaRectangle_New (DiaRectangle *r)
 {
   PyDiaRectangle *self;
 
-  self = PyObject_NEW(PyDiaRectangle, &PyDiaRectangle_Type);
-  if (!self) return NULL;
+  self = PyObject_NEW (PyDiaRectangle, &PyDiaRectangle_Type);
 
-  self->is_int = (ri != NULL);
-  if (self->is_int)
-    self->r.ri = *ri;
-  else
-    self->r.rf = *r;
+  if (!self) {
+    return NULL;
+  }
 
-  return (PyObject *)self;
+  self->r = *r;
+
+  return (PyObject *) self;
 }
 
-PyObject* PyDiaRectangle_New_FromPoints (Point* ul, Point* lr)
+
+PyObject *
+PyDiaRectangle_New_FromPoints (Point *ul, Point *lr)
 {
   PyDiaRectangle *self;
 
-  self = PyObject_NEW(PyDiaRectangle, &PyDiaRectangle_Type);
-  if (!self) return NULL;
+  self = PyObject_NEW (PyDiaRectangle, &PyDiaRectangle_Type);
+
+  if (!self) {
+    return NULL;
+  }
 
-  self->is_int = FALSE;
-  self->r.rf.left = ul->x;
-  self->r.rf.top = ul->y;
-  self->r.rf.right = lr->x;
-  self->r.rf.bottom = lr->y;
+  self->r.left = ul->x;
+  self->r.top = ul->y;
+  self->r.right = lr->x;
+  self->r.bottom = lr->y;
 
-  return (PyObject *)self;
+  return (PyObject *) self;
 }
 
 
@@ -148,113 +152,288 @@ PyDiaMatrix_New (DiaMatrix *matrix)
 
   return (PyObject *)self;
 }
+
+
 /*
  * Dealloc
  */
 static void
-PyDiaGeometry_Dealloc(void *self)
+PyDiaGeometry_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare ?
  */
-static int
-PyDiaPoint_Compare(PyDiaPoint *self,
-                            PyDiaPoint *other)
+static PyObject *
+PyDiaPoint_RichCompare (PyObject *self,
+                        PyObject *other,
+                        int       op)
 {
-#if 1
-  return memcmp (&self->pt, &other->pt, sizeof(Point));
-#else /* ? */
-  if (self->pt.x == other->pt.x && self->pt.x == other->pt.x) return 0;
-#define SQR(pt) (pt.x*pt.y)
-  if (SQR(self->pt) > SQR(other->pt)) return -1;
-#undef  SQR
-  return 1;
-#endif
+  Point *point_a = &((PyDiaPoint *) self)->pt;
+  Point *point_b = &((PyDiaPoint *) other)->pt;
+
+  switch (op) {
+    case Py_EQ:
+      if (fabs (point_a->x - point_b->x) < 0.0001 &&
+          fabs (point_a->y - point_b->y) < 0.0001) {
+        Py_RETURN_TRUE;
+      } else {
+        Py_RETURN_FALSE;
+      }
+      break;
+    case Py_NE:
+      if (fabs (point_a->x - point_b->x) >= 0.0001 &&
+          fabs (point_a->y - point_b->y) >= 0.0001) {
+        Py_RETURN_TRUE;
+      } else {
+        Py_RETURN_FALSE;
+      }
+      break;
+    case Py_LT:
+    case Py_GT:
+    case Py_LE:
+    case Py_GE:
+    default:
+      Py_RETURN_NOTIMPLEMENTED;
+  }
 }
 
-static int
-PyDiaRectangle_Compare(PyDiaRectangle *self,
-                            PyDiaRectangle *other)
+
+static PyObject *
+PyDiaRectangle_RichCompare (PyObject *self,
+                            PyObject *other,
+                            int       op)
 {
-  /* this is not correct */
-  return memcmp (&self->r, &other->r, sizeof(DiaRectangle));
+  DiaRectangle *rect_a = &((PyDiaRectangle *) self)->r;
+  DiaRectangle *rect_b = &((PyDiaRectangle *) other)->r;
+
+  switch (op) {
+    case Py_EQ:
+      if (fabs (rect_a->top - rect_b->top) < 0.0001 &&
+          fabs (rect_a->left - rect_b->left) < 0.0001 &&
+          fabs (rect_a->bottom - rect_b->bottom) < 0.0001 &&
+          fabs (rect_a->right - rect_b->right) < 0.0001) {
+        Py_RETURN_TRUE;
+      } else {
+        Py_RETURN_FALSE;
+      }
+      break;
+    case Py_NE:
+      if (fabs (rect_a->top - rect_b->top) >= 0.0001 &&
+          fabs (rect_a->left - rect_b->left) >= 0.0001 &&
+          fabs (rect_a->bottom - rect_b->bottom) >= 0.0001 &&
+          fabs (rect_a->right - rect_b->right) >= 0.0001) {
+        Py_RETURN_TRUE;
+      } else {
+        Py_RETURN_FALSE;
+      }
+      break;
+    case Py_LT:
+    case Py_GT:
+    case Py_LE:
+    case Py_GE:
+    default:
+      Py_RETURN_NOTIMPLEMENTED;
+  }
 }
 
-static int
-PyDiaBezPoint_Compare(PyDiaBezPoint *self,
-                            PyDiaBezPoint *other)
+
+static PyObject *
+PyDiaBezPoint_RichCompare (PyObject *a,
+                           PyObject *b,
+                           int       op)
 {
-  return memcmp (&self->bpn, &other->bpn, sizeof(BezPoint));
+  PyDiaBezPoint *self = (PyDiaBezPoint *) a;
+  PyDiaBezPoint *other = (PyDiaBezPoint *) b;
+  int cmp = memcmp (&self->bpn, &other->bpn, sizeof (BezPoint));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = cmp == 0 ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = cmp != 0 ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = cmp <= 0 ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = cmp >= 0 ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = cmp < 0 ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = cmp > 0 ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
-static int
-PyDiaArrow_Compare(PyDiaArrow *self,
-                        PyDiaArrow *other)
+
+static PyObject *
+PyDiaArrow_RichCompare (PyObject *a,
+                        PyObject *b,
+                        int       op)
 {
-  return memcmp (&self->arrow, &other->arrow, sizeof(Arrow));
+  PyDiaArrow *self = (PyDiaArrow *) a;
+  PyDiaArrow *other = (PyDiaArrow *) b;
+  int cmp = memcmp (&self->arrow, &other->arrow, sizeof (Arrow));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = cmp == 0 ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = cmp != 0 ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = cmp <= 0 ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = cmp >= 0 ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = cmp < 0 ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = cmp > 0 ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
-static int
-PyDiaMatrix_Compare(PyDiaMatrix *self,
-                   PyDiaMatrix *other)
+
+static PyObject *
+PyDiaMatrix_RichCompare (PyObject *a,
+                         PyObject *b,
+                         int       op)
 {
-  return memcmp (&self->matrix, &other->matrix, sizeof(DiaMatrix));
+  PyDiaMatrix *self = (PyDiaMatrix *) a;
+  PyDiaMatrix *other = (PyDiaMatrix *) b;
+  int cmp = memcmp (&self->matrix, &other->matrix, sizeof (DiaMatrix));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = cmp == 0 ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = cmp != 0 ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = cmp <= 0 ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = cmp >= 0 ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = cmp < 0 ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = cmp > 0 ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaGeometry_Hash(PyObject *self)
+PyDiaGeometry_Hash (PyObject *self)
 {
-    return (long)self;
+  return (long) self;
 }
 
 
 static PyObject *
-PyDiaRectangle_GetAttr(PyDiaRectangle *self, char *attr)
+PyDiaRectangle_GetAttr (PyObject *obj, PyObject *arg)
 {
-#define I_OR_F(v) \
-  (self->is_int ? \
-   PyInt_FromLong(self->r.ri. v) : PyFloat_FromDouble(self->r.rf. v))
+  PyDiaRectangle *self;
+  const char *attr;
 
-  if (!strcmp(attr, "__members__"))
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaRectangle *) obj;
+
+#define I_OR_F(v) PyFloat_FromDouble (self->r.v)
+
+  if (!g_strcmp0 (attr, "__members__")) {
     return Py_BuildValue("[ssss]", "top", "left", "right", "bottom" );
-  else if (!strcmp(attr, "top"))
-    return I_OR_F(top);
-  else if (!strcmp(attr, "left"))
-    return I_OR_F(left);
-  else if (!strcmp(attr, "right"))
-    return I_OR_F(right);
-  else if (!strcmp(attr, "bottom"))
-    return I_OR_F(bottom);
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  } else if (!g_strcmp0 (attr, "top")) {
+    return I_OR_F (top);
+  } else if (!g_strcmp0 (attr, "left")) {
+    return I_OR_F (left);
+  } else if (!g_strcmp0 (attr, "right")) {
+    return I_OR_F (right);
+  } else if (!g_strcmp0 (attr, "bottom")) {
+    return I_OR_F (bottom);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 
 #undef I_O_F
 }
 
 
 static PyObject *
-PyDiaBezPoint_GetAttr(PyDiaBezPoint *self, char *attr)
+PyDiaBezPoint_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[ssss]", "type", "p1", "p2", "p3");
-  else if (!strcmp(attr, "type"))
-    return PyInt_FromLong(self->bpn.type);
-  else if (!strcmp(attr, "p1"))
-    return PyDiaPoint_New(&(self->bpn.p1));
-  else if (!strcmp(attr, "p2"))
-    return PyDiaPoint_New(&(self->bpn.p2));
-  else if (!strcmp(attr, "p3"))
-    return PyDiaPoint_New(&(self->bpn.p3));
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  PyDiaBezPoint *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaBezPoint *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ssss]", "type", "p1", "p2", "p3");
+  } else if (!g_strcmp0 (attr, "type")) {
+    return PyLong_FromLong (self->bpn.type);
+  } else if (!g_strcmp0 (attr, "p1")) {
+    return PyDiaPoint_New (&(self->bpn.p1));
+  } else if (!g_strcmp0 (attr, "p2")) {
+    return PyDiaPoint_New (&(self->bpn.p2));
+  } else if (!g_strcmp0 (attr, "p3")) {
+    return PyDiaPoint_New (&(self->bpn.p3));
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 /*
@@ -267,61 +446,58 @@ PyDiaBezPoint_GetAttr(PyDiaBezPoint *self, char *attr)
  * Repr / _Str
  */
 static PyObject *
-PyDiaPoint_Str (PyDiaPoint *self)
+PyDiaPoint_Str (PyObject *self)
 {
   PyObject *py_s;
 
 #ifndef _DEBUG /* gives crashes with nan */
   char *s = g_strdup_printf ("(%f,%f)",
-                             (float) (self->pt.x),
-                             (float) (self->pt.y));
+                             (float) ((PyDiaPoint *) self)->pt.x,
+                             (float) ((PyDiaPoint *) self)->pt.y);
 #else
   char *s = g_strdup_printf ("(%e,%e)",
-                             (float) (self->pt.x),
-                             (float) (self->pt.y));
+                             (float) ((PyDiaPoint *) self)->pt.x),
+                             (float) ((PyDiaPoint *) self)->pt.y));
 #endif
 
-  py_s = PyString_FromString (s);
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
 
 static PyObject *
-PyDiaRectangle_Str (PyDiaRectangle *self)
+PyDiaRectangle_Str (PyObject *self)
 {
   PyObject *py_s;
   char *s;
 
-  if (self->is_int) {
-    s = g_strdup_printf ("((%d,%d),(%d,%d))",
-                         (self->r.ri.left),
-                         (self->r.ri.top),
-                         (self->r.ri.right),
-                         (self->r.ri.bottom));
-  } else {
+  {
 #ifndef _DEBUG /* gives crashes with nan */
     s = g_strdup_printf ("((%f,%f),(%f,%f))",
-                         (float) (self->r.rf.left),
-                         (float) (self->r.rf.top),
-                         (float) (self->r.rf.right),
-                         (float) (self->r.rf.bottom));
+                         (float) (((PyDiaRectangle *) self)->r.left),
+                         (float) (((PyDiaRectangle *) self)->r.top),
+                         (float) (((PyDiaRectangle *) self)->r.right),
+                         (float) (((PyDiaRectangle *) self)->r.bottom));
 #else
     s = g_strdup_printf ("((%e,%e),(%e,%e))",
-                         (float) (self->r.rf.left),
-                         (float) (self->r.rf.top),
-                         (float) (self->r.rf.right),
-                         (float) (self->r.rf.bottom));
+                         (float) (((PyDiaRectangle *) self)->r.left),
+                         (float) (((PyDiaRectangle *) self)->r.top),
+                         (float) (((PyDiaRectangle *) self)->r.right),
+                         (float) (((PyDiaRectangle *) self)->r.bottom));
 #endif
   }
-  py_s = PyString_FromString (s);
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
 
 static PyObject *
-PyDiaBezPoint_Str(PyDiaBezPoint *self)
+PyDiaBezPoint_Str (PyObject *self)
 {
   PyObject *py_s;
 #if 0 /* FIXME: this is causing bad crashes with unintialized points.
@@ -334,42 +510,48 @@ PyDiaBezPoint_Str(PyDiaBezPoint *self)
                              (self->bpn.type == BEZ_LINE_TO ? "LINE_TO" : "CURVE_TO")));
 #else
   char* s = g_strdup_printf ("%s",
-                             (self->bpn.type == BEZ_MOVE_TO ? "MOVE_TO" :
-                             (self->bpn.type == BEZ_LINE_TO ? "LINE_TO" : "CURVE_TO")));
+                             (((PyDiaBezPoint *) self)->bpn.type == BEZ_MOVE_TO ? "MOVE_TO" :
+                             (((PyDiaBezPoint *) self)->bpn.type == BEZ_LINE_TO ? "LINE_TO" : "CURVE_TO")));
 #endif
-  py_s = PyString_FromString (s);
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
 
 static PyObject *
-PyDiaArrow_Str(PyDiaArrow *self)
+PyDiaArrow_Str (PyObject *self)
 {
-  PyObject* py_s;
-  char* s = g_strdup_printf ("(%f,%f, %d)",
-                             (float) (self->arrow.width),
-                             (float) (self->arrow.length),
-                             (int) (self->arrow.type));
-  py_s = PyString_FromString (s);
+  PyObject *py_s;
+  char *s = g_strdup_printf ("(%f,%f, %d)",
+                             (float) (((PyDiaArrow *) self)->arrow.width),
+                             (float) (((PyDiaArrow *) self)->arrow.length),
+                             (int) (((PyDiaArrow *) self)->arrow.type));
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
 
 static PyObject *
-PyDiaMatrix_Str(PyDiaMatrix *self)
+PyDiaMatrix_Str (PyObject *self)
 {
   PyObject *py_s;
   char *s = g_strdup_printf ("(%f, %f, %f, %f, %f, %f)",
-                             (float) (self->matrix.xx),
-                             (float) (self->matrix.yx),
-                             (float) (self->matrix.xy),
-                             (float) (self->matrix.yy),
-                             (float) (self->matrix.x0),
-                             (float) (self->matrix.y0));
-  py_s = PyString_FromString (s);
+                             (float) (((PyDiaMatrix *) self)->matrix.xx),
+                             (float) (((PyDiaMatrix *) self)->matrix.yx),
+                             (float) (((PyDiaMatrix *) self)->matrix.xy),
+                             (float) (((PyDiaMatrix *) self)->matrix.yy),
+                             (float) (((PyDiaMatrix *) self)->matrix.x0),
+                             (float) (((PyDiaMatrix *) self)->matrix.y0));
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
@@ -452,13 +634,13 @@ rect_item (PyObject *o, gssize i)
 
   switch (i) {
     case 0:
-      return PyDiaRectangle_GetAttr (self, "left");
+      return PyFloat_FromDouble (self->r.left);
     case 1:
-      return PyDiaRectangle_GetAttr (self, "top");
+      return PyFloat_FromDouble (self->r.top);
     case 2:
-      return PyDiaRectangle_GetAttr (self, "right");
+      return PyFloat_FromDouble (self->r.right);
     case 3:
-      return PyDiaRectangle_GetAttr (self, "bottom");
+      return PyFloat_FromDouble (self->r.bottom);
     default :
       PyErr_SetString (PyExc_IndexError, "PyDiaRectangle index out of range");
       return NULL;
@@ -466,37 +648,9 @@ rect_item (PyObject *o, gssize i)
 }
 
 
-static PyObject *
-rect_slice (PyObject* o, gssize i, gssize j)
-{
-  PyObject *ret;
-
-  /* j maybe negative */
-  if (j <= 0) {
-    j = 3 + j;
-  }
-  /* j may be rather huge [:] ^= 0:0x7FFFFFFF */
-  if (j > 3) {
-    j = 3;
-  }
-  ret = PyTuple_New (j - i + 1);
-  if (ret) {
-    for (int k = i; k <= j && k < 4; k++) {
-      PyTuple_SetItem (ret, k - i, rect_item (o, k));
-    }
-  }
-  return ret;
-}
-
 static PySequenceMethods rect_as_sequence = {
-  rect_length,    /*sq_length*/
-  (binaryfunc) 0, /*sq_concat*/
-  0,              /*sq_repeat*/
-  rect_item,      /*sq_item*/
-  rect_slice,     /*sq_slice*/
-  0,              /*sq_ass_item*/
-  0,              /*sq_ass_slice*/
-  (objobjproc) 0  /*sq_contains*/
+  .sq_length = rect_length,
+  .sq_item = rect_item,
 };
 
 
@@ -513,38 +667,21 @@ static PyMemberDef PyDiaPoint_Members[] = {
  * Python objetcs
  */
 PyTypeObject PyDiaPoint_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Point",
-    sizeof(PyDiaPoint),
-    0,
-    (destructor)PyDiaGeometry_Dealloc,
-    (printfunc)0,
-    (getattrfunc)0,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaPoint_Compare,
-    (reprfunc)0,
-    0, /* as_number */
-    &point_as_sequence,
-    0,
-    (hashfunc)PyDiaGeometry_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaPoint_Str,
-    PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "The dia.Point does not only provide access trough it's members but also via a sequence interface.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaPoint_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Point",
+  .tp_basicsize = sizeof (PyDiaPoint),
+  .tp_dealloc = PyDiaGeometry_Dealloc,
+  .tp_richcompare = PyDiaPoint_RichCompare,
+  .tp_as_sequence = &point_as_sequence,
+  .tp_hash = PyDiaGeometry_Hash,
+  .tp_str = PyDiaPoint_Str,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_doc = "The dia.Point does not only provide access trough it's members "
+            "but also via a sequence interface.",
+  .tp_members = PyDiaPoint_Members,
 };
+
+
 #define T_INVALID -1 /* can't allow direct access due to pyobject->is_int */
 static PyMemberDef PyDiaRect_Members[] = {
     { "top", T_INVALID, 0, RESTRICTED|READONLY,
@@ -557,40 +694,24 @@ static PyMemberDef PyDiaRect_Members[] = {
       "int or double: right edge x coordinate" },
     { NULL }
 };
+
+
 PyTypeObject PyDiaRectangle_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Rectangle",
-    sizeof(PyDiaRectangle),
-    0,
-    (destructor)PyDiaGeometry_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaRectangle_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaRectangle_Compare,
-    (reprfunc)0,
-    0, /* as_number */
-    &rect_as_sequence,
-    0, /* as_mapping */
-    (hashfunc)PyDiaGeometry_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaRectangle_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "The dia.Rectangle does not only provide access trough it's members but also via a sequence interface.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaRect_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Rectangle",
+  .tp_basicsize = sizeof (PyDiaRectangle),
+  .tp_dealloc = PyDiaGeometry_Dealloc,
+  .tp_getattro = PyDiaRectangle_GetAttr,
+  .tp_richcompare = PyDiaRectangle_RichCompare,
+  .tp_as_sequence = &rect_as_sequence,
+  .tp_hash = PyDiaGeometry_Hash,
+  .tp_str = PyDiaRectangle_Str,
+  .tp_doc = "The dia.Rectangle does not only provide access trough it's "
+            "members but also via a sequence interface.",
+  .tp_members = PyDiaRect_Members,
 };
 
+
 static PyMemberDef PyDiaBezPoint_Members[] = {
     { "type", T_INT, offsetof(PyDiaBezPoint, bpn.type), 0,
       "int: MOVETO, LINETO using p1 only;  CURVETO all 3 points" },
@@ -602,40 +723,23 @@ static PyMemberDef PyDiaBezPoint_Members[] = {
       "Point: target point for CURVETO" },
     { NULL }
 };
+
+
 PyTypeObject PyDiaBezPoint_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.BezPoint",
-    sizeof(PyDiaBezPoint),
-    0,
-    (destructor)PyDiaGeometry_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaBezPoint_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaBezPoint_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaGeometry_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaBezPoint_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "A dia.Point, a bezier type and two control points (dia.Point) make a bezier point.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaBezPoint_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.BezPoint",
+  .tp_basicsize = sizeof (PyDiaBezPoint),
+  .tp_dealloc = PyDiaGeometry_Dealloc,
+  .tp_getattro = PyDiaBezPoint_GetAttr,
+  .tp_richcompare = PyDiaBezPoint_RichCompare,
+  .tp_hash = PyDiaGeometry_Hash,
+  .tp_str = PyDiaBezPoint_Str,
+  .tp_doc = "A dia.Point, a bezier type and two control points (dia.Point) "
+            "make a bezier point.",
+  .tp_members = PyDiaBezPoint_Members,
 };
 
+
 static PyMemberDef PyDiaArrow_Members[] = {
     { "type", T_INT, offsetof(PyDiaArrow, arrow.type), 0,
       "int: the shape of the arrow" },
@@ -645,40 +749,22 @@ static PyMemberDef PyDiaArrow_Members[] = {
       "double: length along the line" },
     { NULL }
 };
+
+
 PyTypeObject PyDiaArrow_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Arrow",
-    sizeof(PyDiaArrow),
-    0,
-    (destructor)PyDiaGeometry_Dealloc,
-    (printfunc)0,
-    (getattrfunc)0,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaArrow_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaGeometry_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaArrow_Str,
-    PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "Dia's line objects usually ends with an dia.Arrow",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaArrow_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Arrow",
+  .tp_basicsize = sizeof (PyDiaArrow),
+  .tp_dealloc = PyDiaGeometry_Dealloc,
+  .tp_richcompare = PyDiaArrow_RichCompare,
+  .tp_hash = PyDiaGeometry_Hash,
+  .tp_str = PyDiaArrow_Str,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_doc = "Dia's line objects usually ends with an dia.Arrow",
+  .tp_members = PyDiaArrow_Members,
 };
 
+
 static PyMemberDef PyDiaMatrix_Members[] = {
     { "xx", T_DOUBLE, offsetof(PyDiaMatrix, matrix.xx), 0, "double" },
     { "xy", T_DOUBLE, offsetof(PyDiaMatrix, matrix.xy), 0, "double" },
@@ -688,36 +774,17 @@ static PyMemberDef PyDiaMatrix_Members[] = {
     { "y0", T_DOUBLE, offsetof(PyDiaMatrix, matrix.y0), 0, "double" },
     { NULL }
 };
+
+
 PyTypeObject PyDiaMatrix_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Matrix",
-    sizeof(PyDiaMatrix),
-    0,
-    (destructor)PyDiaGeometry_Dealloc,
-    (printfunc)0,
-    (getattrfunc)0,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaMatrix_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaGeometry_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaMatrix_Str,
-    PyObject_GenericGetAttr, /* tp_getattro */
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "Dia's matrix to do affine transformation",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaMatrix_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Matrix",
+  .tp_basicsize = sizeof (PyDiaMatrix),
+  .tp_dealloc = PyDiaGeometry_Dealloc,
+  .tp_richcompare = PyDiaMatrix_RichCompare,
+  .tp_hash = PyDiaGeometry_Hash,
+  .tp_str = PyDiaMatrix_Str,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_doc = "Dia's matrix to do affine transformation",
+  .tp_members = PyDiaMatrix_Members,
 };
diff --git a/plug-ins/python/pydia-geometry.h b/plug-ins/python/pydia-geometry.h
index abc2273b6..2b3171336 100644
--- a/plug-ins/python/pydia-geometry.h
+++ b/plug-ins/python/pydia-geometry.h
@@ -6,18 +6,15 @@
 #include "arrows.h"
 
 typedef struct {
-    PyObject_HEAD
-    union {
-      IntRectangle ri;
-      DiaRectangle rf;
-    } r;
-    gboolean is_int;
+  PyObject_HEAD
+
+  DiaRectangle r;
 } PyDiaRectangle;
 
 extern PyTypeObject PyDiaRectangle_Type;
 
-PyObject* PyDiaRectangle_New (DiaRectangle* r, IntRectangle* ri);
-PyObject* PyDiaRectangle_New_FromPoints (Point* ul, Point* lr);
+PyObject *PyDiaRectangle_New (DiaRectangle* r);
+PyObject *PyDiaRectangle_New_FromPoints (Point* ul, Point* lr);
 
 typedef struct {
     PyObject_HEAD
diff --git a/plug-ins/python/pydia-handle.c b/plug-ins/python/pydia-handle.c
index 3c9811da3..77be5a205 100644
--- a/plug-ins/python/pydia-handle.c
+++ b/plug-ins/python/pydia-handle.c
@@ -39,45 +39,48 @@ PyDiaHandle_New(Handle *handle, DiaObject *owner)
     return (PyObject *)self;
 }
 
+
 static void
-PyDiaHandle_Dealloc(PyDiaHandle *self)
+PyDiaHandle_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaHandle_Compare(PyDiaHandle *self, PyDiaHandle *other)
+
+static PyObject *
+PyDiaHandle_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->handle == other->handle) return 0;
-    if (self->handle > other->handle) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaHandle *) self)->handle,
+                         ((PyDiaHandle *) other)->handle,
+                         op);
 }
 
+
 static long
-PyDiaHandle_Hash(PyDiaHandle *self)
+PyDiaHandle_Hash (PyObject *self)
 {
-    return (long)self->handle;
+  return (long) ((PyDiaHandle *) self)->handle;
 }
 
+
 static PyObject *
-PyDiaHandle_Connect(PyDiaHandle *self, PyObject *args)
+PyDiaHandle_Connect (PyDiaHandle *self, PyObject *args)
 {
   PyObject *obj;
 
-  if (!PyArg_ParseTuple(args, "O:Handle.connect", &obj))
-       return NULL;
+  if (!PyArg_ParseTuple (args, "O:Handle.connect", &obj)) {
+    return NULL;
+  }
 
   if (PyDiaConnectionPoint_Check (obj)) {
-     PyDiaConnectionPoint *o = (PyDiaConnectionPoint *)obj;
-
-     object_connect (self->owner, self->handle, o->cpoint);
-  }
-  else if (obj == Py_None) {
-     object_unconnect (self->handle->connected_to->object, self->handle);
-  }
-  else {
-    PyErr_SetString(PyExc_TypeError,
-                    "Expecting a ConnectionPoint or None to disconnect.");
+    PyDiaConnectionPoint *o = (PyDiaConnectionPoint *)obj;
+
+    object_connect (self->owner, self->handle, o->cpoint);
+  } else if (obj == Py_None) {
+    object_unconnect (self->handle->connected_to->object, self->handle);
+  } else {
+    PyErr_SetString (PyExc_TypeError,
+                     "Expecting a ConnectionPoint or None to disconnect.");
     return NULL;
   }
   Py_INCREF(Py_None);
@@ -85,7 +88,7 @@ PyDiaHandle_Connect(PyDiaHandle *self, PyObject *args)
 }
 
 static PyMethodDef PyDiaHandle_Methods[] = {
-    { "connect", (PyCFunction)PyDiaHandle_Connect, METH_VARARGS, 
+    { "connect", (PyCFunction)PyDiaHandle_Connect, METH_VARARGS,
       "connect(ConnectionPoint: cp) -> None."
       "  Connect object A's handle with object B's connection point. To disconnect a handle pass in None." },
     { NULL, 0, 0, NULL }
@@ -106,60 +109,54 @@ static PyMemberDef PyDiaHandle_Members[] = {
     { NULL }
 };
 
+
 static PyObject *
-PyDiaHandle_GetAttr(PyDiaHandle *self, gchar *attr)
+PyDiaHandle_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[sssss]", "connect_type", "connected_to", "id",
-                            "pos", "type");
-    else if (!strcmp(attr, "id"))
-       return PyInt_FromLong(self->handle->id);
-    else if (!strcmp(attr, "type"))
-       return PyInt_FromLong(self->handle->type);
-    else if (!strcmp(attr, "pos"))
-       return PyDiaPoint_New(&(self->handle->pos));
-    else if (!strcmp(attr, "connect_type"))
-       return PyInt_FromLong(self->handle->connect_type);
-    else if (!strcmp(attr, "connected_to")) {
-       if (self->handle->connected_to)
-           return PyDiaConnectionPoint_New(self->handle->connected_to);
-       Py_INCREF(Py_None);
-       return Py_None;
+  PyDiaHandle *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaHandle *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[sssss]",
+                          "connect_type", "connected_to", "id", "pos", "type");
+  } else if (!g_strcmp0 (attr, "id")) {
+    return PyLong_FromLong (self->handle->id);
+  } else if (!g_strcmp0 (attr, "type")) {
+    return PyLong_FromLong (self->handle->type);
+  } else if (!g_strcmp0 (attr, "pos")) {
+    return PyDiaPoint_New (&(self->handle->pos));
+  } else if (!g_strcmp0 (attr, "connect_type")) {
+    return PyLong_FromLong (self->handle->connect_type);
+  } else if (!g_strcmp0 (attr, "connected_to")) {
+    if (self->handle->connected_to) {
+      return PyDiaConnectionPoint_New (self->handle->connected_to);
     }
+    Py_INCREF (Py_None);
+    return Py_None;
+  }
 
-    return Py_FindMethod(PyDiaHandle_Methods, (PyObject *)self, attr);
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 PyTypeObject PyDiaHandle_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Handle",
-    sizeof(PyDiaHandle),
-    0,
-    (destructor)PyDiaHandle_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaHandle_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaHandle_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaHandle_Hash,
-    (ternaryfunc)0,
-    (reprfunc)0,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "A handle is used to connect objects or for object resizing.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaHandle_Methods, /* tp_methods */
-    PyDiaHandle_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Handle",
+  .tp_basicsize = sizeof (PyDiaHandle),
+  .tp_dealloc = PyDiaHandle_Dealloc,
+  .tp_getattro = PyDiaHandle_GetAttr,
+  .tp_richcompare = PyDiaHandle_RichCompare,
+  .tp_hash = PyDiaHandle_Hash,
+  .tp_doc = "A handle is used to connect objects or for object resizing.",
+  .tp_methods = PyDiaHandle_Methods,
+  .tp_members = PyDiaHandle_Members,
 };
diff --git a/plug-ins/python/pydia-image.c b/plug-ins/python/pydia-image.c
index 6541ff393..c66d98df6 100644
--- a/plug-ins/python/pydia-image.c
+++ b/plug-ins/python/pydia-image.c
@@ -48,30 +48,36 @@ PyDiaImage_New (DiaImage *image)
  * Dealloc
  */
 static void
-PyDiaImage_Dealloc (PyDiaImage *self)
+PyDiaImage_Dealloc (PyObject *self)
 {
-  g_clear_object (&self->image);
+  g_clear_object (&((PyDiaImage *) self)->image);
 
   PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaImage_Compare(PyDiaImage *self,
-                   PyDiaImage *other)
+static PyObject *
+PyDiaImage_RichCompare (PyObject *a,
+                        PyObject *b,
+                        int       op)
 {
-  return memcmp(&(self->image), &(other->image), sizeof(DiaImage *));
+  PyDiaImage *self = (PyDiaImage *) a;
+  PyDiaImage *other = (PyDiaImage *) b;
+
+  Py_RETURN_RICHCOMPARE (self->image, other->image, op);
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaImage_Hash(PyObject *self)
+PyDiaImage_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
 
@@ -79,22 +85,33 @@ PyDiaImage_Hash(PyObject *self)
  * GetAttr
  */
 static PyObject *
-PyDiaImage_GetAttr(PyDiaImage *self, char *attr)
+PyDiaImage_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__")) {
-    return Py_BuildValue("[ssssss]", "width", "height",
-                                    "rgb_data", "mask_data",
-                                    "filename", "uri");
-  } else if (!strcmp(attr, "width"))
-    return PyInt_FromLong(dia_image_width(self->image));
-  else if (!strcmp(attr, "height"))
-    return PyInt_FromLong(dia_image_height(self->image));
-  else if (!strcmp(attr, "filename")) {
-    return PyString_FromString(dia_image_filename(self->image));
-  } else if (!strcmp (attr, "uri")) {
+  PyDiaImage *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaImage *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ssssss]",
+                          "width", "height", "rgb_data", "mask_data",
+                          "filename", "uri");
+  } else if (!g_strcmp0 (attr, "width"))
+    return PyLong_FromLong (dia_image_width(self->image));
+  else if (!g_strcmp0 (attr, "height"))
+    return PyLong_FromLong (dia_image_height(self->image));
+  else if (!g_strcmp0 (attr, "filename")) {
+    return PyUnicode_FromString (dia_image_filename (self->image));
+  } else if (!g_strcmp0 (attr, "uri")) {
     GError *error = NULL;
-    const char *fname = dia_image_filename(self->image);
-    char* s;
+    const char *fname = dia_image_filename (self->image);
+    char *s;
 
     if (g_path_is_absolute (fname)) {
       s = g_filename_to_uri (fname, NULL, &error);
@@ -108,7 +125,7 @@ PyDiaImage_GetAttr(PyDiaImage *self, char *attr)
     }
 
     if (s) {
-      PyObject* py_s = PyString_FromString (s);
+      PyObject *py_s = PyUnicode_FromString (s);
       g_clear_pointer (&s, g_free);
       return py_s;
     } else {
@@ -120,33 +137,33 @@ PyDiaImage_GetAttr(PyDiaImage *self, char *attr)
       }
       return NULL;
     }
-  } else if (!strcmp (attr, "rgb_data")) {
+  } else if (!g_strcmp0 (attr, "rgb_data")) {
     unsigned char *s = dia_image_rgb_data (self->image);
     int len = dia_image_width (self->image) * dia_image_height (self->image) * 3;
     PyObject *py_s;
 
     if (!s) {
-      return PyErr_NoMemory();
+      return PyErr_NoMemory ();
     }
 
-    py_s = PyString_FromStringAndSize ((const char *) s, len);
+    py_s = PyBytes_FromStringAndSize ((const char *) s, len);
     g_clear_pointer (&s, g_free);
     return py_s;
-  } else if (!strcmp (attr, "mask_data")) {
+  } else if (!g_strcmp0 (attr, "mask_data")) {
     unsigned char *s = dia_image_mask_data (self->image);
     int len = dia_image_width (self->image) * dia_image_height (self->image);
     PyObject *py_s;
 
     if (!s) {
-      return PyErr_NoMemory();
+      return PyErr_NoMemory ();
     }
-    py_s = PyString_FromStringAndSize ((const char *) s, len);
+    py_s = PyBytes_FromStringAndSize ((const char *) s, len);
     g_clear_pointer (&s, g_free);
     return py_s;
   }
 
-  PyErr_SetString (PyExc_AttributeError, attr);
-  return NULL;
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
@@ -154,16 +171,18 @@ PyDiaImage_GetAttr(PyDiaImage *self, char *attr)
  * Repr / _Str
  */
 static PyObject *
-PyDiaImage_Str (PyDiaImage *self)
+PyDiaImage_Str (PyObject *self)
 {
-  PyObject* py_s;
-  const char* name = dia_image_filename (self->image);
-  char* s = g_strdup_printf ("%ix%i,%s",
-                             dia_image_width (self->image),
-                             dia_image_height (self->image),
+  PyObject *py_s;
+  const char *name = dia_image_filename (((PyDiaImage *) self)->image);
+  char *s = g_strdup_printf ("%ix%i,%s",
+                             dia_image_width (((PyDiaImage *) self)->image),
+                             dia_image_height (((PyDiaImage *) self)->image),
                              name ? name : "(null)");
-  py_s = PyString_FromString (s);
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
@@ -184,39 +203,20 @@ static PyMemberDef PyDiaImage_Members[] = {
       "string: Uniform Resource Identifier of the image" },
     { NULL }
 };
+
+
 /*
  * Python objetcs
  */
 PyTypeObject PyDiaImage_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Image",
-    sizeof(PyDiaImage),
-    0,
-    (destructor)PyDiaImage_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaImage_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaImage_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaImage_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaImage_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "dia.Image gets passed into DiaRenderer.draw_image",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaImage_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Image",
+  .tp_basicsize = sizeof (PyDiaImage),
+  .tp_dealloc = PyDiaImage_Dealloc,
+  .tp_getattro = PyDiaImage_GetAttr,
+  .tp_richcompare = PyDiaImage_RichCompare,
+  .tp_hash = PyDiaImage_Hash,
+  .tp_str = PyDiaImage_Str,
+  .tp_doc = "dia.Image gets passed into DiaRenderer.draw_image",
+  .tp_members = PyDiaImage_Members,
 };
diff --git a/plug-ins/python/pydia-layer.c b/plug-ins/python/pydia-layer.c
index 9f529e39b..06720d3b3 100644
--- a/plug-ins/python/pydia-layer.c
+++ b/plug-ins/python/pydia-layer.c
@@ -44,34 +44,34 @@ PyDiaLayer_New (DiaLayer *layer)
 
 
 static void
-PyDiaLayer_Dealloc (PyDiaLayer *self)
+PyDiaLayer_Dealloc (PyObject *self)
 {
-  g_clear_object (&self->layer);
+  g_clear_object (&((PyDiaLayer *) self)->layer);
 
   PyObject_DEL (self);
 }
 
 
-static int
-PyDiaLayer_Compare (PyDiaLayer *self, PyDiaLayer *other)
+static PyObject *
+PyDiaLayer_RichCompare (PyObject *self, PyObject *other, int op)
 {
-  if (self->layer == other->layer) return 0;
-  if (self->layer > other->layer) return -1;
-  return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaLayer *) self)->layer,
+                         ((PyDiaLayer *) other)->layer,
+                         op);
 }
 
 
 static long
-PyDiaLayer_Hash (PyDiaLayer *self)
+PyDiaLayer_Hash (PyObject *self)
 {
-  return (long) self->layer;
+  return (long) ((PyDiaLayer *) self)->layer;
 }
 
 
 static PyObject *
-PyDiaLayer_Str (PyDiaLayer *self)
+PyDiaLayer_Str (PyObject *self)
 {
-  return PyString_FromString (dia_layer_get_name (self->layer));
+  return PyUnicode_FromString (dia_layer_get_name (((PyDiaLayer *) self)->layer));
 }
 
 /* methods here */
@@ -99,8 +99,8 @@ PyDiaLayer_ObjectGetIndex (PyDiaLayer *self, PyObject *args)
     return NULL;
   }
 
-  return PyInt_FromLong (dia_layer_object_get_index (self->layer,
-                                                     obj->object));
+  return PyLong_FromLong (dia_layer_object_get_index (self->layer,
+                                                      obj->object));
 }
 
 
@@ -224,7 +224,7 @@ PyDiaLayer_UpdateExtents (PyDiaLayer *self, PyObject *args)
     return NULL;
   }
 
-  return PyInt_FromLong (dia_layer_update_extents (self->layer));
+  return PyLong_FromLong (dia_layer_update_extents (self->layer));
 }
 
 
@@ -313,7 +313,7 @@ PyDiaLayer_GetExtents (PyDiaLayer *self, void *closure)
 static PyObject *
 PyDiaLayer_GetName (PyDiaLayer *self, void *closure)
 {
-  return PyString_FromString (dia_layer_get_name (self->layer));
+  return PyUnicode_FromString (dia_layer_get_name (self->layer));
 }
 
 
@@ -357,14 +357,14 @@ static PyGetSetDef PyDiaLayer_GetSetters[] = {
 
 
 PyTypeObject PyDiaLayer_Type = {
-  PyObject_HEAD_INIT(NULL)
+  PyVarObject_HEAD_INIT (NULL, 0)
   .tp_name = "dia.Layer",
   .tp_basicsize = sizeof (PyDiaLayer),
   .tp_flags = Py_TPFLAGS_DEFAULT,
-  .tp_dealloc = (destructor) PyDiaLayer_Dealloc,
-  .tp_compare = (cmpfunc) PyDiaLayer_Compare,
-  .tp_hash = (hashfunc) PyDiaLayer_Hash,
-  .tp_str = (reprfunc) PyDiaLayer_Str,
+  .tp_dealloc = PyDiaLayer_Dealloc,
+  .tp_richcompare = PyDiaLayer_RichCompare,
+  .tp_hash = PyDiaLayer_Hash,
+  .tp_str = PyDiaLayer_Str,
   .tp_doc = "A Layer is part of a Diagram and can contain objects.",
   .tp_methods = PyDiaLayer_Methods,
   .tp_getset = PyDiaLayer_GetSetters,
diff --git a/plug-ins/python/pydia-menuitem.c b/plug-ins/python/pydia-menuitem.c
index c055fb81c..c108dfc6e 100644
--- a/plug-ins/python/pydia-menuitem.c
+++ b/plug-ins/python/pydia-menuitem.c
@@ -47,46 +47,64 @@ PyDiaMenuitem_New (const DiaMenuItem *menuitem)
  * Dealloc
  */
 static void
-PyDiaMenuitem_Dealloc(PyDiaMenuitem *self)
+PyDiaMenuitem_Dealloc (PyObject *self)
 {
   /* we dont own the object */
-  PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaMenuitem_Compare(PyDiaMenuitem *self,
-                      PyDiaMenuitem *other)
+static PyObject *
+PyDiaMenuitem_RichCompare (PyObject *self,
+                           PyObject *other,
+                           int       op)
 {
-  return (self->menuitem == other->menuitem);
+  Py_RETURN_RICHCOMPARE (((PyDiaMenuitem *) self)->menuitem,
+                         ((PyDiaMenuitem *) other)->menuitem,
+                         op);
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaMenuitem_Hash(PyObject *self)
+PyDiaMenuitem_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
+
 /*
  * GetAttr
  */
 static PyObject *
-PyDiaMenuitem_GetAttr(PyDiaMenuitem *self, gchar *attr)
+PyDiaMenuitem_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[ss]", "text", "active");
-  else if (!strcmp(attr, "text"))
-    return PyString_FromString(self->menuitem->text);
-  else if (!strcmp(attr, "active"))
-    return PyInt_FromLong(self->menuitem->active);
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  PyDiaMenuitem *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaMenuitem *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ss]", "text", "active");
+  } else if (!g_strcmp0 (attr, "text")) {
+    return PyUnicode_FromString (self->menuitem->text);
+  } else if (!g_strcmp0 (attr, "active")) {
+    return PyLong_FromLong (self->menuitem->active);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
@@ -120,19 +138,23 @@ PyDiaMenuitem_Call (PyDiaMenuitem *self, PyObject *args)
  * Repr / _Str
  */
 static PyObject *
-PyDiaMenuitem_Str(PyDiaMenuitem *self)
+PyDiaMenuitem_Str (PyObject *obj)
 {
-  PyObject* py_s;
-  gchar* s = g_strdup_printf ("%s - %s,%s,%s",
-                              self->menuitem->text,
-                              self->menuitem->active & DIAMENU_ACTIVE ? "active" : "inactive",
-                              self->menuitem->active & DIAMENU_TOGGLE ? "toggle" : "",
-                              self->menuitem->active & DIAMENU_TOGGLE_ON ? "on" : "");
-  py_s = PyString_FromString (s);
+  PyDiaMenuitem *self = (PyDiaMenuitem *) obj;
+  PyObject *py_s;
+  char *s = g_strdup_printf ("%s - %s,%s,%s",
+                             self->menuitem->text,
+                             self->menuitem->active & DIAMENU_ACTIVE ? "active" : "inactive",
+                             self->menuitem->active & DIAMENU_TOGGLE ? "toggle" : "",
+                             self->menuitem->active & DIAMENU_TOGGLE_ON ? "on" : "");
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
+
 static PyMethodDef PyDiaMenuitem_Methods[] = {
     { "call", (PyCFunction)PyDiaMenuitem_Call, METH_VARARGS,
       "call() -> None."
@@ -148,39 +170,21 @@ static PyMemberDef PyDiaMenuitem_Members[] = {
       "boolean: if it is callable" },
     { NULL }
 };
+
+
 /*
  * Python objetcs
  */
 PyTypeObject PyDiaMenuitem_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Menuitem",
-    sizeof(PyDiaMenuitem),
-    0,
-    (destructor)PyDiaMenuitem_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaMenuitem_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaMenuitem_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaMenuitem_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaMenuitem_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "dia.Menuitem is holding menu functions for dia.Object",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaMenuitem_Methods, /* tp_methods */
-    PyDiaMenuitem_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Menuitem",
+  .tp_basicsize = sizeof (PyDiaMenuitem),
+  .tp_dealloc = PyDiaMenuitem_Dealloc,
+  .tp_getattro = PyDiaMenuitem_GetAttr,
+  .tp_richcompare = PyDiaMenuitem_RichCompare,
+  .tp_hash = PyDiaMenuitem_Hash,
+  .tp_str = PyDiaMenuitem_Str,
+  .tp_doc = "dia.Menuitem is holding menu functions for dia.Object",
+  .tp_methods = PyDiaMenuitem_Methods,
+  .tp_members = PyDiaMenuitem_Members,
 };
diff --git a/plug-ins/python/pydia-object.c b/plug-ins/python/pydia-object.c
index d7d9baa73..f76e67477 100644
--- a/plug-ins/python/pydia-object.c
+++ b/plug-ins/python/pydia-object.c
@@ -41,34 +41,37 @@ PyDiaObject_New(DiaObject *object)
     return (PyObject *)self;
 }
 
+
 static void
-PyDiaObject_Dealloc(PyDiaObject *self)
+PyDiaObject_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaObject_Compare(PyDiaObject *self, PyDiaObject *other)
+
+static PyObject *
+PyDiaObject_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->object == other->object) return 0;
-    if (self->object > other->object) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaObject *) self)->object,
+                         ((PyDiaObject *) other)->object,
+                         op);
 }
 
+
 static long
-PyDiaObject_Hash(PyDiaObject *self)
+PyDiaObject_Hash (PyObject *self)
 {
-    return (long)self->object;
+  return (long) ((PyDiaObject *) self)->object;
 }
 
 
 static PyObject *
-PyDiaObject_Str (PyDiaObject *self)
+PyDiaObject_Str (PyObject *self)
 {
   char *strname = g_strdup_printf ("<DiaObject of type \"%s\" at %lx>",
-                                  self->object->type->name,
-                                  (long) self->object);
-  PyObject *ret = PyString_FromString (strname);
+                                  ((PyDiaObject *) self)->object->type->name,
+                                  (long) ((PyDiaObject *) self)->object);
+  PyObject *ret = PyUnicode_FromString (strname);
 
   g_clear_pointer (&strname, g_free);
   return ret;
@@ -185,7 +188,7 @@ PyDiaObject_GetMenu(PyDiaObject *self, PyObject *args)
     }    ;
 
     ret = PyTuple_New (2);
-    PyTuple_SetItem(ret, 0, PyString_FromString (m->title ? m->title : ""));
+    PyTuple_SetItem(ret, 0, PyUnicode_FromString (m->title ? m->title : ""));
     items = PyList_New(0);
     for (i = 0; i < m->num_items; ++i) {
         DiaMenuItem *mi = &m->items[i];
@@ -308,127 +311,134 @@ static PyMemberDef PyDiaObject_Members[] = {
 
 
 static PyObject *
-PyDiaObject_GetAttr (PyDiaObject *self, char *attr)
+PyDiaObject_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[sssss]", "bounding_box", "connections",
-                            "handles", "parent", "properties", "type");
-    else if (!strcmp(attr, "type"))
-       return PyDiaObjectType_New(self->object->type);
-    else if (!strcmp(attr, "bounding_box"))
-       return PyDiaRectangle_New(&(self->object->bounding_box), NULL);
-    else if (!strcmp(attr, "handles")) {
-       int i;
-       PyObject *ret = PyTuple_New(self->object->num_handles);
-
-       for (i = 0; i < self->object->num_handles; i++)
-           PyTuple_SetItem(ret, i, PyDiaHandle_New(self->object->handles[i], self->object));
-       return ret;
-    } else if (!strcmp(attr, "connections")) {
-       int i;
-       PyObject *ret = PyTuple_New(self->object->num_connections);
-
-       for (i = 0; i < self->object->num_connections; i++)
-           PyTuple_SetItem(ret, i, PyDiaConnectionPoint_New(
-                               self->object->connections[i]));
-       return ret;
-    } else if (!strcmp(attr, "properties")) {
-       return PyDiaProperties_New(self->object);
-    } else if (!strcmp(attr, "parent")) {
-       if (!self->object->parent) {
-           Py_INCREF(Py_None);
-           return Py_None;
-       } else {
-           return PyDiaObject_New(self->object->parent);
-       }
+  PyDiaObject *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaObject *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[sssss]",
+                          "bounding_box", "connections", "handles", "parent",
+                          "properties", "type");
+  } else if (!g_strcmp0 (attr, "type")) {
+    return PyDiaObjectType_New (self->object->type);
+  } else if (!g_strcmp0 (attr, "bounding_box")) {
+    return PyDiaRectangle_New (&(self->object->bounding_box));
+  } else if (!g_strcmp0 (attr, "handles")) {
+    int i;
+    PyObject *ret = PyTuple_New (self->object->num_handles);
+
+    for (i = 0; i < self->object->num_handles; i++) {
+      PyTuple_SetItem(ret, i, PyDiaHandle_New(self->object->handles[i], self->object));
     }
 
-    return Py_FindMethod(PyDiaObject_Methods, (PyObject *)self, attr);
+    return ret;
+  } else if (!g_strcmp0 (attr, "connections")) {
+    int i;
+    PyObject *ret = PyTuple_New(self->object->num_connections);
+
+    for (i = 0; i < self->object->num_connections; i++) {
+      PyTuple_SetItem (ret, i,
+                       PyDiaConnectionPoint_New (self->object->connections[i]));
+    }
+    return ret;
+  } else if (!g_strcmp0 (attr, "properties")) {
+    return PyDiaProperties_New(self->object);
+  } else if (!g_strcmp0 (attr, "parent")) {
+    if (!self->object->parent) {
+      Py_INCREF (Py_None);
+      return Py_None;
+    } else {
+      return PyDiaObject_New (self->object->parent);
+    }
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 PyTypeObject PyDiaObject_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Object",
-    sizeof(PyDiaObject),
-    0,
-    (destructor)PyDiaObject_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaObject_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaObject_Compare,
-    (reprfunc)PyDiaObject_Str,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaObject_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaObject_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "The main building block of diagrams.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaObject_Methods, /* tp_methods */
-    PyDiaObject_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Object",
+  .tp_basicsize = sizeof (PyDiaObject),
+  .tp_dealloc = PyDiaObject_Dealloc,
+  .tp_getattro = PyDiaObject_GetAttr,
+  .tp_richcompare = PyDiaObject_RichCompare,
+  .tp_repr = PyDiaObject_Str,
+  .tp_hash = PyDiaObject_Hash,
+  .tp_str = PyDiaObject_Str,
+  .tp_doc = "The main building block of diagrams.",
+  .tp_methods = PyDiaObject_Methods,
+  .tp_members = PyDiaObject_Members,
 };
 
+
 PyObject *
-PyDiaObjectType_New(DiaObjectType *otype)
+PyDiaObjectType_New (DiaObjectType *otype)
 {
-    PyDiaObjectType *self;
+  PyDiaObjectType *self;
 
-    self = PyObject_NEW(PyDiaObjectType, &PyDiaObjectType_Type);
+  self = PyObject_NEW (PyDiaObjectType, &PyDiaObjectType_Type);
 
-    if (!self) return NULL;
-    self->otype = otype;
-    return (PyObject *)self;
+  if (!self) {
+    return NULL;
+  }
+
+  self->otype = otype;
+
+  return (PyObject *) self;
 }
 
+
 static void
-PyDiaObjectType_Dealloc(PyDiaObjectType *self)
+PyDiaObjectType_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaObjectType_Compare(PyDiaObjectType *self, PyDiaObjectType *other)
+
+static PyObject *
+PyDiaObjectType_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->otype == other->otype) return 0;
-    if (self->otype > other->otype) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaObjectType *) self)->otype,
+                         ((PyDiaObjectType *) other)->otype,
+                         op);
 }
 
+
 static long
-PyDiaObjectType_Hash(PyDiaObjectType *self)
+PyDiaObjectType_Hash (PyObject *self)
 {
-    return (long)self->otype;
+  return (long) ((PyDiaObjectType *) self)->otype;
 }
 
 
 static PyObject *
-PyDiaObjectType_Repr(PyDiaObjectType *self)
+PyDiaObjectType_Repr (PyObject *self)
 {
   char *strname = g_strdup_printf ("<DiaObjectType \"%s\">",
-                                   self->otype->name);
-  PyObject *ret = PyString_FromString (strname);
+                                   ((PyDiaObjectType *) self)->otype->name);
+  PyObject *ret = PyUnicode_FromString (strname);
 
   g_clear_pointer (&strname, g_free);
+
   return ret;
 }
 
 
 static PyObject *
-PyDiaObjectType_Str(PyDiaObjectType *self)
+PyDiaObjectType_Str (PyObject *self)
 {
-    return PyString_FromString(self->otype->name);
+  return PyUnicode_FromString (((PyDiaObjectType *) self)->otype->name);
 }
 
 
@@ -497,52 +507,45 @@ static PyMemberDef PyDiaObjectType_Members[] = {
 
 
 static PyObject *
-PyDiaObjectType_GetAttr (PyDiaObjectType *self, char *attr)
+PyDiaObjectType_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp (attr, "__members__")) {
+  PyDiaObjectType *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaObjectType *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
     return Py_BuildValue ("[ss]", "name", "version");
-  } else if (!strcmp (attr, "name")) {
-    return PyString_FromString (self->otype->name);
-  } else if (!strcmp (attr, "version")) {
-    return PyInt_FromLong (self->otype->version);
+  } else if (!g_strcmp0 (attr, "name")) {
+    return PyUnicode_FromString (self->otype->name);
+  } else if (!g_strcmp0 (attr, "version")) {
+    return PyLong_FromLong (self->otype->version);
   }
 
-  return Py_FindMethod (PyDiaObjectType_Methods, (PyObject *) self, attr);
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
 PyTypeObject PyDiaObjectType_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.ObjectType",
-    sizeof(PyDiaObjectType),
-    0,
-    (destructor)PyDiaObjectType_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaObjectType_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaObjectType_Compare,
-    (reprfunc)PyDiaObjectType_Repr,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaObjectType_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaObjectType_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "The dia.Object factory. Allows to create objects of the specific type. "
-    "Use: factory = get_object_type(<type name>) to get a grip on it.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaObjectType_Methods, /* tp_methods */
-    PyDiaObjectType_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.ObjectType",
+  .tp_basicsize = sizeof(PyDiaObjectType),
+  .tp_dealloc = PyDiaObjectType_Dealloc,
+  .tp_getattro = PyDiaObjectType_GetAttr,
+  .tp_richcompare = PyDiaObjectType_RichCompare,
+  .tp_repr = PyDiaObjectType_Repr,
+  .tp_hash = PyDiaObjectType_Hash,
+  .tp_str = PyDiaObjectType_Str,
+  .tp_doc = "The dia.Object factory. Allows to create objects of the "
+            "specific type. Use: factory = get_object_type(<type name>) to "
+            "get a grip on it.",
+  .tp_methods = PyDiaObjectType_Methods,
+  .tp_members = PyDiaObjectType_Members,
 };
-
diff --git a/plug-ins/python/pydia-paperinfo.c b/plug-ins/python/pydia-paperinfo.c
index cb47501fe..de3812560 100644
--- a/plug-ins/python/pydia-paperinfo.c
+++ b/plug-ins/python/pydia-paperinfo.c
@@ -47,29 +47,63 @@ PyDiaPaperinfo_New (const PaperInfo *paper)
  * Dealloc
  */
 static void
-PyDiaPaperinfo_Dealloc(PyDiaPaperinfo *self)
+PyDiaPaperinfo_Dealloc (PyObject *self)
 {
   /* we dont own the object */
-  PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaPaperinfo_Compare(PyDiaPaperinfo *self,
-                   PyDiaPaperinfo *other)
+static PyObject *
+PyDiaPaperinfo_Compare (PyObject *a,
+                        PyObject *b,
+                        int       op)
 {
-  return memcmp(&(self->paper), &(other->paper), sizeof(PaperInfo));
+  PyDiaPaperinfo *self = (PyDiaPaperinfo *) a;
+  PyDiaPaperinfo *other = (PyDiaPaperinfo *) b;
+  int cmp = memcmp (&self->paper, &other->paper, sizeof (DiaMatrix));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = cmp == 0 ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = cmp != 0 ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = cmp <= 0 ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = cmp >= 0 ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = cmp < 0 ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = cmp > 0 ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaPaperinfo_Hash(PyObject *self)
+PyDiaPaperinfo_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
 
@@ -77,25 +111,36 @@ PyDiaPaperinfo_Hash(PyObject *self)
  * GetAttr
  */
 static PyObject *
-PyDiaPaperinfo_GetAttr(PyDiaPaperinfo *self, char *attr)
+PyDiaPaperinfo_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[sssss]", "name", "is_portrait",
-                                    "scaling",
-                                    "width", "height");
-  else if (!strcmp(attr, "name"))
-    return PyString_FromString(self->paper->name);
-  else if (!strcmp(attr, "is_portrait"))
-    return PyInt_FromLong(self->paper->is_portrait);
-  else if (!strcmp(attr, "scaling"))
-    return PyFloat_FromDouble(self->paper->scaling);
-  else if (!strcmp(attr, "width"))
-    return PyFloat_FromDouble(self->paper->width);
-  else if (!strcmp(attr, "height"))
-    return PyFloat_FromDouble(self->paper->height);
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  PyDiaPaperinfo *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaPaperinfo *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[sssss]",
+                          "name", "is_portrait", "scaling", "width", "height");
+  } else if (!g_strcmp0 (attr, "name")) {
+    return PyUnicode_FromString (self->paper->name);
+  } else if (!g_strcmp0 (attr, "is_portrait")) {
+    return PyLong_FromLong (self->paper->is_portrait);
+  } else if (!g_strcmp0 (attr, "scaling")) {
+    return PyFloat_FromDouble (self->paper->scaling);
+  } else if (!g_strcmp0 (attr, "width")) {
+    return PyFloat_FromDouble (self->paper->width);
+  } else if (!g_strcmp0 (attr, "height")) {
+    return PyFloat_FromDouble (self->paper->height);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
@@ -103,16 +148,19 @@ PyDiaPaperinfo_GetAttr(PyDiaPaperinfo *self, char *attr)
  * Repr / _Str
  */
 static PyObject *
-PyDiaPaperinfo_Str(PyDiaPaperinfo *self)
+PyDiaPaperinfo_Str (PyObject *obj)
 {
-  PyObject* py_s;
-  char* s = g_strdup_printf ("%s - %fx%f %f%%",
+  PyDiaPaperinfo *self = (PyDiaPaperinfo *) obj;
+  PyObject *py_s;
+  char *s = g_strdup_printf ("%s - %fx%f %f%%",
                              self->paper->name ? self->paper->name : "(null)",
                              self->paper->width,
                              self->paper->height,
                              self->paper->scaling);
-  py_s = PyString_FromString (s);
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
@@ -131,39 +179,20 @@ static PyMemberDef PyDiaPaperinfo_Members[] = {
       "real: height of the drawable area (sans margins)" },
     { NULL }
 };
+
+
 /*
  * Python objetcs
  */
 PyTypeObject PyDiaPaperinfo_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Paperinfo",
-    sizeof(PyDiaPaperinfo),
-    0,
-    (destructor)PyDiaPaperinfo_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaPaperinfo_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaPaperinfo_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaPaperinfo_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaPaperinfo_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "dia.Paperinfo is part of dia.DiagramData describing the paper",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaPaperinfo_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Paperinfo",
+  .tp_basicsize = sizeof (PyDiaPaperinfo),
+  .tp_dealloc = PyDiaPaperinfo_Dealloc,
+  .tp_getattro = PyDiaPaperinfo_GetAttr,
+  .tp_richcompare = PyDiaPaperinfo_Compare,
+  .tp_hash = PyDiaPaperinfo_Hash,
+  .tp_str = PyDiaPaperinfo_Str,
+  .tp_doc = "dia.Paperinfo is part of dia.DiagramData describing the paper",
+  .tp_members = PyDiaPaperinfo_Members,
 };
diff --git a/plug-ins/python/pydia-properties.c b/plug-ins/python/pydia-properties.c
index c0bef0c3b..d406a02a1 100644
--- a/plug-ins/python/pydia-properties.c
+++ b/plug-ins/python/pydia-properties.c
@@ -41,135 +41,155 @@ PyDiaProperties_New (DiaObject* obj)
   return (PyObject *)self;
 }
 
+
 /*
  * Dealloc
  */
 static void
-PyDiaProperties_Dealloc(PyDiaObject *self)
+PyDiaProperties_Dealloc (PyObject *self)
 {
-  self->object = NULL; /* XXX: should dec ref */
-  PyObject_DEL(self);
+  ((PyDiaObject *) self)->object = NULL; /* XXX: should dec ref */
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaProperties_Compare(PyDiaProperties *self,
-                      PyDiaProperties *other)
+static PyObject *
+PyDiaProperties_RichCompare (PyObject *self,
+                             PyObject *other,
+                             int       op)
 {
-  return memcmp(&(self->object), &(other->object), sizeof(DiaObject*));
+  Py_RETURN_RICHCOMPARE (((PyDiaProperties *) self)->object,
+                         ((PyDiaProperties *) other)->object,
+                         op);
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaProperties_Hash(PyObject *self)
+PyDiaProperties_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
+
 /*
  * Repr / _Str
  */
 static PyObject *
-PyDiaProperties_Str(PyDiaProperties *self)
+PyDiaProperties_Str (PyObject *self)
 {
   PyObject* py_s;
   char* s = g_strdup_printf ("<DiaProperties at %p of DiaObject at %p>",
-                             self, self->object );
-  py_s = PyString_FromString (s);
+                             self, ((PyDiaObject *) self)->object);
+
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
+
 /*
  * Implement the dictionary interface for the Properties object
  */
 static PyObject *
-PyDiaProperties_Get(PyDiaProperties *self, PyObject *args)
+PyDiaProperties_Get (PyObject *self, PyObject *args)
 {
   PyObject *key;
   PyObject *failobj = Py_None;
   PyObject *val = NULL;
 
-  if (!PyArg_ParseTuple(args, "O|O:get", &key, &failobj))
+  if (!PyArg_ParseTuple (args, "O|O:get", &key, &failobj))
     return NULL;
 
-  if (self->object->ops->get_props != NULL) {
+  if (((PyDiaObject *) self)->object->ops->get_props != NULL) {
     Property *p;
-    char* name = PyString_AsString(key);
-    p = object_prop_by_name (self->object, name);
+    const char *name = PyUnicode_AsUTF8 (key);
+
+    p = object_prop_by_name (((PyDiaObject *) self)->object, name);
+
     if (p) {
-      val = PyDiaProperty_New(p); /* makes a copy */
+      val = PyDiaProperty_New (p); /* makes a copy */
       p->ops->free(p);
     }
   }
 
   if (val == NULL) {
     val = failobj;
-    Py_INCREF(val);
+    Py_INCREF (val);
   }
 
   return val;
 }
 
+
 static PyObject *
-PyDiaProperties_HasKey(PyDiaProperties *self, PyObject *args)
+PyDiaProperties_HasKey (PyDiaProperties *self, PyObject *args)
 {
   PyObject *key;
   long ok = 0;
 
-  if (!PyArg_ParseTuple(args, "O:has_key", &key))
+  if (!PyArg_ParseTuple (args, "O:has_key", &key)) {
     return NULL;
+  }
 
-  if (!PyString_Check(key))
+  if (!PyUnicode_Check (key)) {
     ok = 0; /* is this too drastic? */
+  }
 
   if (self->object->ops->get_props != NULL) {
     Property *p;
-    char     *name;
+    const char *name;
 
-    name = PyString_AsString(key);
+    name = PyUnicode_AsUTF8 (key);
 
     p = object_prop_by_name (self->object, name);
     ok = (NULL != p);
-    if (p)
+    if (p) {
       p->ops->free(p);
+    }
   }
 
-  return PyInt_FromLong(ok);
+  return PyLong_FromLong (ok);
 }
 
+
 static PyObject *
-PyDiaProperties_Keys(PyDiaProperties *self, PyObject *args)
+PyDiaProperties_Keys (PyDiaProperties *self, PyObject *args)
 {
   PyObject *list;
   const PropDescription *desc = NULL;
 
-  if (!PyArg_NoArgs(args))
-    return NULL;
+  list = PyList_New (0);
 
-  list = PyList_New(0);
+  // TODO: Why the check?
+  if (self->object->ops->describe_props) {
+    desc = dia_object_describe_properties (self->object);
+  }
 
-  if (self->object->ops->describe_props)
-    desc = self->object->ops->describe_props(self->object);
   if (desc) {
-    int i;
-    for (i = 0; desc[i].name; i++) {
+    for (int i = 0; desc[i].name; i++) {
       /* at the moment I see no use case to access widgets from Python, PROP_FLAG_LOAD_ONLY compatibility 
not anted here */
-      if ((desc[i].flags & (PROP_FLAG_WIDGET_ONLY|PROP_FLAG_LOAD_ONLY)) == 0)
-        PyList_Append(list, PyString_FromString(desc[i].name));
+      if ((desc[i].flags & (PROP_FLAG_WIDGET_ONLY|PROP_FLAG_LOAD_ONLY)) == 0) {
+        PyList_Append (list, PyUnicode_FromString (desc[i].name));
+      }
     }
   }
 
   return list;
 }
 
+
 static Py_ssize_t
-PyDiaProperties_Length (PyDiaProperties* self)
+PyDiaProperties_Length (PyObject *obj)
 {
+  PyDiaProperties *self = (PyDiaProperties *) obj;
+
   if (self->nprops < 0) {
     const PropDescription *desc = NULL;
 
@@ -189,24 +209,25 @@ PyDiaProperties_Length (PyDiaProperties* self)
   return self->nprops;
 }
 
+
 static PyObject *
-PyDiaProperties_Subscript (PyDiaProperties *self, register PyObject *key)
+PyDiaProperties_Subscript (PyObject *obj, PyObject *key)
 {
+  PyDiaProperties *self = (PyDiaProperties *) obj;
   PyObject *v = NULL;
 
   if (!self->object->ops->describe_props) {
-    PyErr_SetObject(PyExc_KeyError, key);
+    PyErr_SetObject (PyExc_KeyError, key);
     return NULL;
-  }
-  else {
+  } else {
     Property *p;
-    char     *name;
+    const char *name;
 
-    name = PyString_AsString(key);
+    name = PyUnicode_AsUTF8 (key);
     p = object_prop_by_name (self->object, name);
 
     if (p) {
-      v = PyDiaProperty_New(p); /* makes a copy */
+      v = PyDiaProperty_New (p); /* makes a copy */
       p->ops->free (p);
     }
   }
@@ -217,19 +238,20 @@ PyDiaProperties_Subscript (PyDiaProperties *self, register PyObject *key)
   return v;
 }
 
+
 static int
-PyDiaProperties_AssSub (PyDiaProperties* self, PyObject *key, PyObject *val)
+PyDiaProperties_AssSub (PyObject *obj, PyObject *key, PyObject *val)
 {
+  PyDiaProperties *self = (PyDiaProperties *) obj;
   int ret = -1;
 
   if (val == NULL) {
-    PyErr_SetString(PyExc_TypeError, "can not delete properties.");
-  }
-  else {
+    PyErr_SetString (PyExc_TypeError, "can not delete properties.");
+  } else {
     Property *p;
-    char     *name;
+    const char *name;
 
-    name = PyString_AsString(key);
+    name = PyUnicode_AsUTF8 (key);
     p = object_prop_by_name (self->object, name);
 
     /* g_print ("AssSub(key: '%s', type <%s>)\n", name, (p ? p->type : "none")); */
@@ -251,69 +273,83 @@ PyDiaProperties_AssSub (PyDiaProperties* self, PyObject *key, PyObject *val)
   return ret;
 }
 
+
+static PyObject *
+PyDiaProperties_Item (PyObject *o, gssize i)
+{
+  PyDiaProperties *self = (PyDiaProperties *) o;
+  PyObject *v = NULL;
+  Property *p;
+  GPtrArray *props;
+
+  if (i > self->nprops || i < 0) {
+    PyErr_SetString (PyExc_IndexError, "PyDiaProperties index out of range");
+    return NULL;
+  }
+
+  props = g_ptr_array_new ();
+
+  object_get_props (self->object, props);
+
+  p = g_ptr_array_index (props, i);
+
+  if (p) {
+    v = PyDiaProperty_New (p); /* makes a copy */
+    p->ops->free (p);
+  }
+
+  g_ptr_array_unref (props);
+
+  return v;
+}
+
+
 static PyMappingMethods PyDiaProperties_AsMapping = {
-       (lenfunc)PyDiaProperties_Length, /*mp_length*/
-       (binaryfunc)PyDiaProperties_Subscript, /*mp_subscript*/
-       (objobjargproc)PyDiaProperties_AssSub, /*mp_ass_subscript*/
+  .mp_length = PyDiaProperties_Length,
+  .mp_subscript = PyDiaProperties_Subscript,
+  .mp_ass_subscript = PyDiaProperties_AssSub,
+};
+
+
+static PySequenceMethods PyDiaProperties_AsSequence = {
+  .sq_length = PyDiaProperties_Length,
+  .sq_item = PyDiaProperties_Item,
 };
 
+
 static PyMethodDef PyDiaProperties_Methods[] = {
-       /* duck-typing dictionary */
-       {"get",     (PyCFunction)PyDiaProperties_Get,     METH_VARARGS},
-       {"has_key", (PyCFunction)PyDiaProperties_HasKey,  METH_VARARGS},
-       {"keys",    (PyCFunction)PyDiaProperties_Keys},
-       {NULL,          NULL}           /* sentinel */
+  /* duck-typing dictionary */
+  {"get",     (PyCFunction) PyDiaProperties_Get,    METH_VARARGS},
+  {"has_key", (PyCFunction) PyDiaProperties_HasKey, METH_VARARGS},
+  {"keys",    (PyCFunction) PyDiaProperties_Keys,   METH_NOARGS},
+  {NULL, NULL}
 };
 
+
 #define T_INVALID -1 /* can't allow direct access due to pyobject->handle indirection */
 static PyMemberDef PyDiaProperties_Members[] = {
-       { NULL }
+  { NULL }
 };
 
-/*
- * GetAttr
- */
-static PyObject*
-PyDiaProperties_GetAttr(PyDiaProperties *self, char *attr)
-{
-  return Py_FindMethod(PyDiaProperties_Methods, (PyObject *)self, attr);
-}
 
 /*
  * Python object
  */
 PyTypeObject PyDiaProperties_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Properties",
-    sizeof(PyDiaProperties),
-    0,
-    (destructor)PyDiaProperties_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaProperties_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaProperties_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    &PyDiaProperties_AsMapping, /* PyMappingMethods */
-    (hashfunc)PyDiaProperties_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaProperties_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "A dictionary interface to dia.Object's standard properties. Many properties"
-    "can be get and set through this. If there is a specific method to change an"
-    "objects property like o.move() or o.move_handle() use that instead.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaProperties_Methods, /* tp_methods */
-    PyDiaProperties_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Properties",
+  .tp_basicsize = sizeof (PyDiaProperties),
+  .tp_dealloc = PyDiaProperties_Dealloc,
+  .tp_getattro = PyObject_GenericGetAttr,
+  .tp_richcompare = PyDiaProperties_RichCompare,
+  .tp_as_mapping = &PyDiaProperties_AsMapping,
+  .tp_as_sequence = &PyDiaProperties_AsSequence,
+  .tp_hash = PyDiaProperties_Hash,
+  .tp_str = PyDiaProperties_Str,
+  .tp_doc = "A dictionary interface to dia.Object's standard properties. "
+            "Many properties can be get and set through this. If there is "
+            "a specific method to change an objects property like o.move() "
+            "or o.move_handle() use that instead.",
+  .tp_methods = PyDiaProperties_Methods,
+  .tp_members = PyDiaProperties_Members,
 };
diff --git a/plug-ins/python/pydia-properties.h b/plug-ins/python/pydia-properties.h
index 6e45e86ea..e3d64c88b 100644
--- a/plug-ins/python/pydia-properties.h
+++ b/plug-ins/python/pydia-properties.h
@@ -7,7 +7,7 @@
 
 typedef struct {
     PyObject_HEAD
-    Property* property; 
+    Property* property;
 } PyDiaProperty;
 
 extern PyTypeObject PyDiaProperty_Type;
@@ -22,7 +22,7 @@ typedef struct {
 extern PyTypeObject PyDiaProperties_Type;
 PyObject* PyDiaProperties_New (DiaObject* obj);
 
-int PyDiaProperty_ApplyToObject (DiaObject *object, gchar *key, Property *prop, PyObject *val);
+int PyDiaProperty_ApplyToObject (DiaObject *object, const char *key, Property *prop, PyObject *val);
 
 #define PyDiaProperty_Check(o) ((o)->ob_type == &PyDiaProperty_Type)
 
diff --git a/plug-ins/python/pydia-property.c b/plug-ins/python/pydia-property.c
index 1792aca37..ff7c5634f 100644
--- a/plug-ins/python/pydia-property.c
+++ b/plug-ins/python/pydia-property.c
@@ -60,36 +60,72 @@ PyObject* PyDiaProperty_New (Property* property)
   return (PyObject *)self;
 }
 
+
 /*
  * Dealloc
  */
 static void
-PyDiaProperty_Dealloc(PyDiaProperty *self)
+PyDiaProperty_Dealloc (PyObject *obj)
 {
-  self->property->ops->free(self->property);
-  PyObject_DEL(self);
+  PyDiaProperty *self = (PyDiaProperty *) obj;
+  self->property->ops->free (self->property);
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaProperty_Compare(PyDiaProperty *self,
-                      PyDiaProperty *other)
+static PyObject *
+PyDiaProperty_RichCompare (PyObject *a,
+                           PyObject *b,
+                           int       op)
 {
-  /* ??? */
-  return memcmp(&(self->property), &(other->property), sizeof(Property));
+  PyDiaProperty *self = (PyDiaProperty *) a;
+  PyDiaProperty *other = (PyDiaProperty *) b;
+  int cmp = memcmp (&self->property, &other->property, sizeof (Property));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = cmp == 0 ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = cmp != 0 ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = cmp <= 0 ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = cmp >= 0 ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = cmp < 0 ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = cmp > 0 ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaProperty_Hash(PyObject *self)
+PyDiaProperty_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
+
 /*
  * Still not convinced that this is better than an integral
  * property->type and some casting ...
@@ -100,13 +136,30 @@ PyDiaProperty_Hash(PyObject *self)
 typedef PyObject * (*PyDiaPropGetFunc) (Property*);
 typedef int (*PyDiaPropSetFunc) (Property*, PyObject *val);
 
-static PyObject * PyDia_get_Char (CharProperty *prop)
-{ return PyInt_FromLong(prop->char_data); }
-static PyObject * PyDia_get_Bool (BoolProperty *prop)
-{ return PyInt_FromLong(prop->bool_data); }
-static PyObject * PyDia_get_Int (IntProperty *prop)
-{ return PyInt_FromLong(prop->int_data); }
-static PyObject * PyDia_get_IntArray (IntarrayProperty *prop)
+
+static PyObject *
+PyDia_get_Char (CharProperty *prop)
+{
+  return PyLong_FromLong (prop->char_data);
+}
+
+
+static PyObject *
+PyDia_get_Bool (BoolProperty *prop)
+{
+  return PyLong_FromLong (prop->bool_data);
+}
+
+
+static PyObject *
+PyDia_get_Int (IntProperty *prop)
+{
+  return PyLong_FromLong (prop->int_data);
+}
+
+
+static PyObject *
+PyDia_get_IntArray (IntarrayProperty *prop)
 {
   PyObject *ret;
   int i, num;
@@ -114,51 +167,98 @@ static PyObject * PyDia_get_IntArray (IntarrayProperty *prop)
   num = prop->intarray_data->len;
   ret = PyTuple_New (num);
 
-  for (i = 0; i < num; i++)
-    PyTuple_SetItem(ret, i,
-                    PyInt_FromLong(g_array_index(prop->intarray_data, gint, i)));
+  for (i = 0; i < num; i++) {
+    PyTuple_SetItem (ret, i,
+                     PyLong_FromLong (g_array_index (prop->intarray_data, int, i)));
+  }
+
   return ret;
 }
-static PyObject * PyDia_get_Enum (EnumProperty *prop)
-{ return PyInt_FromLong(prop->enum_data); }
-static PyObject * PyDia_get_LineStyle (LinestyleProperty *prop)
+
+
+static PyObject *
+PyDia_get_Enum (EnumProperty *prop)
+{
+  return PyLong_FromLong (prop->enum_data);
+}
+
+
+static PyObject *
+PyDia_get_LineStyle (LinestyleProperty *prop)
 {
   PyObject *ret;
 
   ret = PyTuple_New (2);
-  PyTuple_SetItem(ret, 0, PyInt_FromLong(prop->style));
-  PyTuple_SetItem(ret, 1, PyFloat_FromDouble(prop->dash));
+  PyTuple_SetItem (ret, 0, PyLong_FromLong (prop->style));
+  PyTuple_SetItem (ret, 1, PyFloat_FromDouble (prop->dash));
   return ret;
 }
-static PyObject * PyDia_get_Real (RealProperty *prop)
-{ return PyFloat_FromDouble(prop->real_data); }
-static PyObject * PyDia_get_Length (LengthProperty *prop)
-{ return PyFloat_FromDouble(prop->length_data); }
-static PyObject * PyDia_get_Fontsize (FontsizeProperty *prop)
-{ return PyFloat_FromDouble(prop->fontsize_data); }
-static PyObject * PyDia_get_String (StringProperty *prop)
+
+
+static PyObject *
+PyDia_get_Real (RealProperty *prop)
 {
-  if (NULL == prop->string_data)
-    return PyString_FromString("(NULL)");
-  else if (1 == prop->num_lines)
-    return PyString_FromString(prop->string_data);
-  else /* FIXME: MULTISTRING ?  */
-    return PyString_FromString(prop->string_data);
+  return PyFloat_FromDouble (prop->real_data);
+}
+
+
+static PyObject *
+PyDia_get_Length (LengthProperty *prop)
+{
+  return PyFloat_FromDouble (prop->length_data);
+}
+
+
+static PyObject *
+PyDia_get_Fontsize (FontsizeProperty *prop)
+{
+  return PyFloat_FromDouble (prop->fontsize_data);
+}
+
+
+static PyObject *
+PyDia_get_String (StringProperty *prop)
+{
+  if (NULL == prop->string_data) {
+    return PyUnicode_FromString ("(NULL)");
+  } else if (1 == prop->num_lines) {
+    return PyUnicode_FromString (prop->string_data);
+  } else {
+    /* FIXME: MULTISTRING ?  */
+    return PyUnicode_FromString (prop->string_data);
+  }
 }
-static PyObject * PyDia_get_StringList (StringListProperty *prop)
+
+
+static PyObject *
+PyDia_get_StringList (StringListProperty *prop)
 {
   GList *tmp;
-  PyObject *ret = PyList_New(0);
+  PyObject *ret = PyList_New (0);
 
-  for (tmp = prop->string_list; tmp; tmp = tmp->next)
-    PyList_Append(ret, PyString_FromString(tmp->data));
+  for (tmp = prop->string_list; tmp; tmp = tmp->next) {
+    PyList_Append (ret, PyUnicode_FromString (tmp->data));
+  }
   return ret;
 }
-static PyObject * PyDia_get_Text (TextProperty *prop)
-{ return PyDiaText_New (prop->text_data, &prop->attr); }
-static PyObject * PyDia_get_Point (PointProperty *prop)
-{ return PyDiaPoint_New (&prop->point_data); }
-static PyObject * PyDia_get_PointArray (PointarrayProperty *prop)
+
+
+static PyObject *
+PyDia_get_Text (TextProperty *prop)
+{
+  return PyDiaText_New (prop->text_data, &prop->attr);
+}
+
+
+static PyObject *
+PyDia_get_Point (PointProperty *prop)
+{
+  return PyDiaPoint_New (&prop->point_data);
+}
+
+
+static PyObject *
+PyDia_get_PointArray (PointarrayProperty *prop)
 {
   PyObject *ret;
   int i, num;
@@ -166,38 +266,77 @@ static PyObject * PyDia_get_PointArray (PointarrayProperty *prop)
   num = prop->pointarray_data->len;
   ret = PyTuple_New (num);
 
-  for (i = 0; i < num; i++)
-    PyTuple_SetItem(ret, i,
-                    PyDiaPoint_New(&g_array_index(prop->pointarray_data,
-                                   Point, i)));
+  for (i = 0; i < num; i++) {
+    PyTuple_SetItem (ret, i,
+                     PyDiaPoint_New (&g_array_index(prop->pointarray_data,
+                                                    Point, i)));
+  }
+
   return ret;
 }
-static PyObject * PyDia_get_BezPoint (BezPointProperty *prop)
-{ return PyDiaBezPoint_New (&prop->bezpoint_data); }
-static PyObject * PyDia_get_BezPointArray (BezPointarrayProperty *prop)
+
+
+static PyObject *
+PyDia_get_BezPoint (BezPointProperty *prop)
+{
+  return PyDiaBezPoint_New (&prop->bezpoint_data);
+}
+
+
+static PyObject *
+PyDia_get_BezPointArray (BezPointarrayProperty *prop)
 {
   PyObject *ret;
-  int i, num;
+  int num;
 
   num = prop->bezpointarray_data->len;
   ret = PyTuple_New (num);
 
-  for (i = 0; i < num; i++)
-    PyTuple_SetItem(ret, i,
-                    PyDiaBezPoint_New(&g_array_index(prop->bezpointarray_data,
-                                      BezPoint, i)));
+  for (int i = 0; i < num; i++) {
+    PyTuple_SetItem (ret, i,
+                     PyDiaBezPoint_New (&g_array_index (prop->bezpointarray_data,
+                                                        BezPoint,
+                                                        i)));
+  }
+
   return ret;
 }
-static PyObject * PyDia_get_Rect (RectProperty *prop)
-{ return PyDiaRectangle_New (&prop->rect_data, NULL); }
-static PyObject * PyDia_get_Arrow (ArrowProperty *prop)
-{ return PyDiaArrow_New (&prop->arrow_data); }
-static PyObject * PyDia_get_Matrix (MatrixProperty *prop)
-{ return PyDiaMatrix_New (prop->matrix); }
-static PyObject * PyDia_get_Color (ColorProperty *prop)
-{ return PyDiaColor_New (&prop->color_data); }
-static PyObject * PyDia_get_Font (FontProperty *prop)
-{ return PyDiaFont_New (prop->font_data); }
+
+
+static PyObject *
+PyDia_get_Rect (RectProperty *prop)
+{
+  return PyDiaRectangle_New (&prop->rect_data);
+}
+
+
+static PyObject *
+PyDia_get_Arrow (ArrowProperty *prop)
+{
+  return PyDiaArrow_New (&prop->arrow_data);
+}
+
+
+static PyObject *
+PyDia_get_Matrix (MatrixProperty *prop)
+{
+  return PyDiaMatrix_New (prop->matrix);
+}
+
+
+static PyObject *
+PyDia_get_Color (ColorProperty *prop)
+{
+  return PyDiaColor_New (&prop->color_data);
+}
+
+
+static PyObject *
+PyDia_get_Font (FontProperty *prop)
+{
+  return PyDiaFont_New (prop->font_data);
+}
+
 
 static PyObject * PyDia_get_Array (ArrayProperty *prop);
 
@@ -210,72 +349,92 @@ static int PyDia_set_Pixbuf (Property *prop, PyObject *val);
 static int
 PyDia_set_Bool (Property *prop, PyObject *val)
 {
-  BoolProperty *p = (BoolProperty *)prop;
-  if (PyInt_Check(val)) {
-    p->bool_data = !!PyInt_AsLong(val);
+  BoolProperty *p = (BoolProperty *) prop;
+
+  if (PyLong_Check (val)) {
+    p->bool_data = !!PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
+
+
 static int
 PyDia_set_Int (Property *prop, PyObject *val)
 {
-  IntProperty *p = (IntProperty *)prop;
-  if (PyInt_Check(val)) {
-    p->int_data = PyInt_AsLong(val);
+  IntProperty *p = (IntProperty *) prop;
+
+  if (PyLong_Check (val)) {
+    p->int_data = PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
+
+
 static int
-PyDia_set_IntArray(Property *prop, PyObject *val)
-{
-  IntarrayProperty *p = (IntarrayProperty *)prop;
-  if (PyTuple_Check(val)) {
-    int i, len = PyTuple_Size(val);
-    g_array_set_size(p->intarray_data, len);
-    for (i = 0; i < len; i++) {
-      PyObject *o = PyTuple_GetItem(val, i);
-      g_array_index(p->intarray_data, gint, i) = PyInt_Check(o) ? PyInt_AsLong(o) : 0;
+PyDia_set_IntArray (Property *prop, PyObject *val)
+{
+  IntarrayProperty *p = (IntarrayProperty *) prop;
+
+  if (PyTuple_Check (val)) {
+    int len = PyTuple_Size (val);
+    g_array_set_size (p->intarray_data, len);
+
+    for (int i = 0; i < len; i++) {
+      PyObject *o = PyTuple_GetItem (val, i);
+      g_array_index (p->intarray_data, int, i) = PyLong_Check (o) ? PyLong_AsLong (o) : 0;
     }
+
     return 0;
-  } else if (PyList_Check(val)) {
-    int i, len = PyList_Size(val);
-    g_array_set_size(p->intarray_data, len);
-    for (i = 0; i < len; i++) {
+  } else if (PyList_Check (val)) {
+    int len = PyList_Size (val);
+    g_array_set_size (p->intarray_data, len);
+
+    for (int i = 0; i < len; i++) {
       PyObject *o = PyList_GetItem(val, i);
-      g_array_index(p->intarray_data, gint, i) = PyInt_Check(o) ? PyInt_AsLong(o) : 0;
+      g_array_index (p->intarray_data, int, i) = PyLong_Check (o) ? PyLong_AsLong (o) : 0;
     }
+
     return 0;
   }
+
   return -1;
 }
+
+
 static int
 PyDia_set_Enum (Property *prop, PyObject *val)
 {
-  EnumProperty *p = (EnumProperty *)prop;
+  EnumProperty *p = (EnumProperty *) prop;
+
   /* XXX a range check would not hurt */
-  if (PyInt_Check(val)) {
-    p->enum_data = PyInt_AsLong(val);
+  if (PyLong_Check (val)) {
+    p->enum_data = PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
+
+
 static int
 PyDia_set_Arrow (Property *prop, PyObject *val)
 {
   ArrowProperty *p = (ArrowProperty *)prop;
 
   if (val->ob_type == &PyDiaArrow_Type) {
-    p->arrow_data = ((PyDiaArrow *)val)->arrow;
+    p->arrow_data = ((PyDiaArrow *) val)->arrow;
     return 0;
   } else if (PyTuple_Check (val)) {
-    int len = PyTuple_Size(val);
+    int len = PyTuple_Size (val);
     PyObject *o;
     if (len < 3)
       return -1;
-    if ((o = PyTuple_GetItem(val, 0)) != NULL && PyInt_Check(o))
-      p->arrow_data.type = PyInt_AsLong(o);
+    if ((o = PyTuple_GetItem(val, 0)) != NULL && PyLong_Check (o))
+      p->arrow_data.type = PyLong_AsLong (o);
     else
       p->arrow_data.type = ARROW_NONE;
     if ((o = PyTuple_GetItem(val, 1)) != NULL && PyFloat_Check(o))
@@ -290,6 +449,8 @@ PyDia_set_Arrow (Property *prop, PyObject *val)
   }
   return -1;
 }
+
+
 static int
 PyDia_set_Matrix (Property *prop, PyObject *val)
 {
@@ -298,7 +459,7 @@ PyDia_set_Matrix (Property *prop, PyObject *val)
   if (val->ob_type == &PyDiaMatrix_Type) {
     if (!p->matrix)
       p->matrix = g_new0 (DiaMatrix, 1);
-    *p->matrix = ((PyDiaMatrix *)val)->matrix;
+    *p->matrix = ((PyDiaMatrix *) val)->matrix;
     return 0;
   }
   return -1;
@@ -308,11 +469,13 @@ PyDia_set_Matrix (Property *prop, PyObject *val)
 static int
 PyDia_set_Color (Property *prop, PyObject *val)
 {
-  ColorProperty *p = (ColorProperty*)prop;
-  if (PyString_Check(val)) {
-    char *str = PyString_AsString(val);
+  ColorProperty *p = (ColorProperty*) prop;
+
+  if (PyUnicode_Check (val)) {
+    const char *str = PyUnicode_AsUTF8 (val);
     PangoColor color;
-    if (pango_color_parse(&color, str)) {
+
+    if (pango_color_parse (&color, str)) {
       p->color_data.red = color.red / 65535.0;
       p->color_data.green = color.green / 65535.0;
       p->color_data.blue = color.blue / 65535.0;
@@ -322,83 +485,108 @@ PyDia_set_Color (Property *prop, PyObject *val)
       g_debug ("%s: Failed to parse color string '%s'", G_STRLOC, str);
     }
   } else if (PyTuple_Check (val)) {
-    int i, len = PyTuple_Size(val);
-    real f[3];
-    if (len < 3)
+    int len = PyTuple_Size (val);
+    double f[3];
+
+    if (len < 3) {
       return -1;
-    for (i = 0; i < 3; i++) {
-      PyObject *o = PyTuple_GetItem(val, i);
-      if (PyFloat_Check(o))
-       f[i] = PyFloat_AsDouble(o);
-      else if (PyInt_Check(o))
-        f[i] = PyInt_AsLong(o) / 65535.;
-      else
-       f[i] = 0.0;
     }
+    for (int i = 0; i < 3; i++) {
+      PyObject *o = PyTuple_GetItem (val, i);
+
+      if (PyFloat_Check (o)) {
+        f[i] = PyFloat_AsDouble (o);
+      } else if (PyLong_Check (o)) {
+        f[i] = PyLong_AsLong (o) / 65535.;
+      } else {
+        f[i] = 0.0;
+      }
+    }
+
     p->color_data.red = f[0];
     p->color_data.green = f[1];
     p->color_data.blue = f[2];
     p->color_data.alpha = 1.0;
+
     return 0;
   }
+
   /* also convert char/255 ? */
   return -1;
 }
+
+
 static int
-PyDia_set_LineStyle(Property *prop, PyObject *val)
+PyDia_set_LineStyle (Property *prop, PyObject *val)
 {
-  LinestyleProperty *p = (LinestyleProperty*)prop;
-  if (PyTuple_Check(val) && PyTuple_Size(val) == 2) {
-    p->style = PyInt_AsLong(PyTuple_GetItem(val, 0));
-    p->dash  = PyFloat_Check(PyTuple_GetItem(val, 1)) ? PyFloat_AsDouble(PyTuple_GetItem(val, 1)) : 
PyInt_AsLong(PyTuple_GetItem(val, 1));
+  LinestyleProperty *p = (LinestyleProperty *) prop;
+
+  if (PyTuple_Check (val) && PyTuple_Size (val) == 2) {
+    p->style = PyLong_AsLong (PyTuple_GetItem (val, 0));
+    p->dash  = PyFloat_Check (PyTuple_GetItem (val, 1)) ?
+          PyFloat_AsDouble (PyTuple_GetItem (val, 1)) :
+                PyLong_AsLong (PyTuple_GetItem (val, 1));
     return 0;
   }
+
   return -1;
 }
+
+
 static int
-PyDia_set_Real(Property *prop, PyObject *val)
+PyDia_set_Real (Property *prop, PyObject *val)
 {
-  RealProperty *p = (RealProperty *)prop;
-  if (PyFloat_Check(val)) {
-    p->real_data = PyFloat_AsDouble(val);
+  RealProperty *p = (RealProperty *) prop;
+
+  if (PyFloat_Check (val)) {
+    p->real_data = PyFloat_AsDouble (val);
     return 0;
-  } else if (PyInt_Check(val)) {
+  } else if (PyLong_Check (val)) {
     /* be tolerant for up-casting */
-    p->real_data = PyInt_AsLong(val);
+    p->real_data = PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
+
+
 /* as of this writing the only difference between Real-, Length- and Fontsize-property
  * is the widget representing them. But that may change so here are the 'type-safe'
  * accessors.
  */
 static int
-PyDia_set_Length(Property *prop, PyObject *val)
+PyDia_set_Length (Property *prop, PyObject *val)
 {
-  LengthProperty *p = (LengthProperty *)prop;
-  if (PyFloat_Check(val)) {
-    p->length_data = PyFloat_AsDouble(val);
+  LengthProperty *p = (LengthProperty *) prop;
+
+  if (PyFloat_Check (val)) {
+    p->length_data = PyFloat_AsDouble (val);
     return 0;
-  } else if (PyInt_Check(val)) {
+  } else if (PyLong_Check (val)) {
     /* be tolerant for up-casting */
-    p->length_data = PyInt_AsLong(val);
+    p->length_data = PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
+
+
 static int
-PyDia_set_Fontsize(Property *prop, PyObject *val)
+PyDia_set_Fontsize (Property *prop, PyObject *val)
 {
-  FontsizeProperty *p = (FontsizeProperty *)prop;
-  if (PyFloat_Check(val)) {
-    p->fontsize_data = PyFloat_AsDouble(val);
+  FontsizeProperty *p = (FontsizeProperty *) prop;
+
+  if (PyFloat_Check (val)) {
+    p->fontsize_data = PyFloat_AsDouble (val);
     return 0;
-  } else if (PyInt_Check(val)) {
+  } else if (PyLong_Check (val)) {
     /* be tolerant for up-casting */
-    p->fontsize_data = PyInt_AsLong(val);
+    p->fontsize_data = PyLong_AsLong (val);
     return 0;
   }
+
   return -1;
 }
 
@@ -406,26 +594,18 @@ PyDia_set_Fontsize(Property *prop, PyObject *val)
 static int
 PyDia_set_String (Property *prop, PyObject *val)
 {
-  StringProperty *p = (StringProperty *)prop;
+  StringProperty *p = (StringProperty *) prop;
 
   if (Py_None == val) {
     /* XXX: maybe a little dangerous, string = NULL not handle in everystring prop */
     g_clear_pointer (&p->string_data, g_free);
     p->num_lines = 0;
     return 0;
-  } else if (PyString_Check (val)) {
-    char *str = PyString_AsString (val);
-    g_clear_pointer (&p->string_data, g_free);
-    p->string_data = g_strdup (str);
-    p->num_lines = 1;
-    return 0;
   } else if (PyUnicode_Check (val)) {
-    PyObject *uval = PyUnicode_AsUTF8String (val);
-    char *str = PyString_AsString(uval);
+    const char *str = PyUnicode_AsUTF8 (val);
     g_clear_pointer (&p->string_data, g_free);
     p->string_data = g_strdup (str);
     p->num_lines = 1;
-    Py_DECREF (uval);
     return 0;
   }
 
@@ -438,19 +618,12 @@ PyDia_set_Text (Property *prop, PyObject *val)
 {
   TextProperty *p = (TextProperty *)prop;
 
-  if (PyString_Check (val)) {
-    char *str = PyString_AsString (val);
+  if (PyUnicode_Check (val)) {
+    const char *str = PyUnicode_AsUTF8 (val);
     g_clear_pointer (&p->text_data, g_free);
     p->text_data = g_strdup (str);
     /* XXX: update size calculation ? */
     return 0;
-  } else if (PyUnicode_Check (val)) {
-    PyObject *uval = PyUnicode_AsUTF8String (val);
-    char *str = PyString_AsString(uval);
-    g_clear_pointer (&p->text_data, g_free);
-    p->text_data = g_strdup (str);
-    Py_DECREF (uval);
-    return 0;
   }
 
   return -1;
@@ -478,14 +651,14 @@ PyDia_set_PointArray (Property *prop, PyObject *val)
   /* accept either tuple or list */
   PointarrayProperty *ptp = (PointarrayProperty *) prop;
 
-  if (PyTuple_Check(val) || PyList_Check(val)) {
+  if (PyTuple_Check (val) || PyList_Check (val)) {
     Point pt;
-    gboolean is_list = !PyTuple_Check(val);
-    int i, len = is_list ? PyList_Size(val) : PyTuple_Size(val);
+    gboolean is_list = !PyTuple_Check (val);
+    int len = is_list ? PyList_Size (val) : PyTuple_Size (val);
     gboolean is_flat = TRUE;
     g_array_set_size(ptp->pointarray_data,len);
-    for (i = 0; i < len; i++) {
-      PyObject *o = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i);
+    for (int i = 0; i < len; i++) {
+      PyObject *o = is_list ? PyList_GetItem (val, i) : PyTuple_GetItem (val, i);
       if (PyTuple_Check(o)) {
         pt.x = PyFloat_AsDouble (PyTuple_GetItem (o, 0));
         pt.y = PyFloat_AsDouble (PyTuple_GetItem (o, 1));
@@ -493,46 +666,57 @@ PyDia_set_PointArray (Property *prop, PyObject *val)
         is_flat = FALSE;
       } else {
         if (i % 2) {
-          pt.x = PyFloat_AsDouble(PyTuple_GetItem(val, i - 1));
-          pt.y = PyFloat_AsDouble(PyTuple_GetItem(val, i));
+          pt.x = PyFloat_AsDouble (PyTuple_GetItem (val, i - 1));
+          pt.y = PyFloat_AsDouble (PyTuple_GetItem (val, i));
           g_array_index (ptp->pointarray_data, Point, i / 2) = pt;
         }
       }
     }
-    if (is_flat)
-      g_array_set_size(ptp->pointarray_data,len/2);
+
+    if (is_flat) {
+      g_array_set_size (ptp->pointarray_data, len / 2);
+    }
+
     return 0;
   }
+
   return -1;
 }
+
+
 static int
-PyDia_set_BezPointArray(Property *prop, PyObject *val)
+PyDia_set_BezPointArray (Property *prop, PyObject *val)
 {
   /* accept either tuple or list */
-  BezPointarrayProperty *ptp = (BezPointarrayProperty *)prop;
-  if (PyTuple_Check(val) || PyList_Check(val)) {
+  BezPointarrayProperty *ptp = (BezPointarrayProperty *) prop;
+
+  if (PyTuple_Check (val) || PyList_Check (val)) {
     BezPoint bpt;
-    gboolean is_list = !PyTuple_Check(val);
-    int i, len = is_list ? PyList_Size(val) : PyTuple_Size(val);
+    gboolean is_list = !PyTuple_Check (val);
+    int len = is_list ? PyList_Size (val) : PyTuple_Size (val);
     int numpts = 0;
-    g_array_set_size(ptp->bezpointarray_data,len);
-    for (i = 0; i < len; i++) {
+
+    g_array_set_size (ptp->bezpointarray_data, len);
+
+    for (int i = 0; i < len; i++) {
       /* a tuple of at least (int,double,double) */
-      PyObject *o = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i);
-      int tp = PyInt_AsLong(PyTuple_GetItem(o, 0));
+      PyObject *o = is_list ? PyList_GetItem (val, i) : PyTuple_GetItem (val, i);
+      int tp = PyLong_AsLong (PyTuple_GetItem (o, 0));
+
+      bpt.p1.x = PyFloat_AsDouble (PyTuple_GetItem (o, 1));
+      bpt.p1.y = PyFloat_AsDouble (PyTuple_GetItem (o, 2));
 
-      bpt.p1.x = PyFloat_AsDouble(PyTuple_GetItem(o, 1));
-      bpt.p1.y = PyFloat_AsDouble(PyTuple_GetItem(o, 2));
       if (BEZ_CURVE_TO == tp) {
         bpt.type = BEZ_CURVE_TO;
-        bpt.p2.x = PyFloat_AsDouble(PyTuple_GetItem(o, 3));
-        bpt.p2.y = PyFloat_AsDouble(PyTuple_GetItem(o, 4));
-        bpt.p3.x = PyFloat_AsDouble(PyTuple_GetItem(o, 5));
-        bpt.p3.y = PyFloat_AsDouble(PyTuple_GetItem(o, 6));
+        bpt.p2.x = PyFloat_AsDouble (PyTuple_GetItem (o, 3));
+        bpt.p2.y = PyFloat_AsDouble (PyTuple_GetItem (o, 4));
+        bpt.p3.x = PyFloat_AsDouble (PyTuple_GetItem (o, 5));
+        bpt.p3.y = PyFloat_AsDouble (PyTuple_GetItem (o, 6));
       } else {
         if (0 == i && tp != BEZ_MOVE_TO) {
           g_debug ("%s: First bezpoint must be BEZ_MOVE_TO", G_STRLOC);
         }
+
         if (0 < i && tp != BEZ_LINE_TO) {
           g_debug ("%s: Further bezpoint must be BEZ_LINE_TO or BEZ_CURVE_TO",
                    G_STRLOC);
@@ -542,35 +726,43 @@ PyDia_set_BezPointArray(Property *prop, PyObject *val)
         /* not strictly needed */
         bpt.p2 = bpt.p3 = bpt.p1;
       }
-      g_array_index(ptp->bezpointarray_data,BezPoint,i) = bpt;
+      g_array_index (ptp->bezpointarray_data, BezPoint, i) = bpt;
       ++numpts;
     }
+
     /* rather than crashing Dia with too few point handle it here */
     if (numpts < 2) {
       PyErr_Warn (PyExc_RuntimeWarning, "Too few BezPoints!");
       return -1;
     }
+
     /* only count valid points */
-    g_array_set_size(ptp->bezpointarray_data,numpts);
+    g_array_set_size (ptp->bezpointarray_data, numpts);
+
     return 0;
   }
+
   return -1;
 }
+
+
 static int
-PyDia_set_Rect(Property *prop, PyObject *val)
-{
-  RectProperty *p = (RectProperty*)prop;
-  if (PyTuple_Check(val) && PyTuple_Size(val) == 4) {
-    p->rect_data.left = PyFloat_AsDouble(PyTuple_GetItem(val, 0));
-    p->rect_data.top = PyFloat_AsDouble(PyTuple_GetItem(val, 1));
-    p->rect_data.right = PyFloat_AsDouble(PyTuple_GetItem(val, 2));
-    p->rect_data.bottom = PyFloat_AsDouble(PyTuple_GetItem(val, 3));
+PyDia_set_Rect (Property *prop, PyObject *val)
+{
+  RectProperty *p = (RectProperty*) prop;
+
+  if (PyTuple_Check (val) && PyTuple_Size (val) == 4) {
+    p->rect_data.left = PyFloat_AsDouble (PyTuple_GetItem (val, 0));
+    p->rect_data.top = PyFloat_AsDouble (PyTuple_GetItem (val, 1));
+    p->rect_data.right = PyFloat_AsDouble (PyTuple_GetItem (val, 2));
+    p->rect_data.bottom = PyFloat_AsDouble (PyTuple_GetItem (val, 3));
     return 0;
   }
+
   return -1;
 }
 
-static int PyDia_set_Array(Property*, PyObject*);
+static int PyDia_set_Array (Property *, PyObject *);
 
 struct {
   char *type;
@@ -609,19 +801,21 @@ struct {
   { PROP_TYPE_PIXBUF, PyDia_get_Pixbuf, PyDia_set_Pixbuf }
 };
 
+
 static void
-ensure_quarks(void)
+ensure_quarks (void)
 {
   static gboolean type_quarks_calculated = FALSE;
-  int i;
+
   if (!type_quarks_calculated) {
-    for (i = 0; i < G_N_ELEMENTS(prop_type_map); i++) {
+    for (int i = 0; i < G_N_ELEMENTS (prop_type_map); i++) {
       prop_type_map[i].quark = g_quark_from_string (prop_type_map[i].type);
     }
     type_quarks_calculated = TRUE;
   }
 }
 
+
 static PyObject *
 PyDia_get_Array (ArrayProperty *prop)
 {
@@ -634,90 +828,101 @@ PyDia_get_Array (ArrayProperty *prop)
 
   /* fill it with tuples or single types */
   if (num > 0) {
-    PyDiaPropGetFunc *getters = g_new0(PyDiaPropGetFunc, num_props);
-    int i;
+    PyDiaPropGetFunc *getters = g_new0 (PyDiaPropGetFunc, num_props);
 
     /* resolve the getter functions once */
-    for (i = 0; i < num_props; i++) {
-      int j;
-      for (j = 0; j < G_N_ELEMENTS(prop_type_map); j++) {
-        Property *inner = g_ptr_array_index(prop->ex_props, i);
-        if (prop_type_map[j].quark == inner->type_quark)
+    for (int i = 0; i < num_props; i++) {
+      for (int j = 0; j < G_N_ELEMENTS (prop_type_map); j++) {
+        Property *inner = g_ptr_array_index (prop->ex_props, i);
+        if (prop_type_map[j].quark == inner->type_quark) {
           getters[i] = (PyDiaPropGetFunc)prop_type_map[j].propget;
+        }
       }
     }
-    for (i = 0; i < num; i++) {
+
+    for (int i = 0; i < num; i++) {
       PyObject *o;
-      GPtrArray *p = g_ptr_array_index(prop->records, i);
+      GPtrArray *p = g_ptr_array_index (prop->records, i);
       int j = 0;
 
       if (1 == num_props) {
-        Property *sub = g_ptr_array_index(p,j);
+        Property *sub = g_ptr_array_index (p, j);
         o = getters[j](sub);
       } else {
         o = PyTuple_New (num_props);
         for (j = 0; j < num_props; j++) {
-          Property *sub = g_ptr_array_index(p,j);
-          PyTuple_SetItem(o, j, getters[j](sub));
+          Property *sub = g_ptr_array_index (p,j);
+          PyTuple_SetItem (o, j, getters[j](sub));
         }
       }
-      PyTuple_SetItem(ret, i, o);
+
+      PyTuple_SetItem (ret, i, o);
     }
+
     g_clear_pointer (&getters, g_free);
   }
 
   return ret;
 }
 
+
 static int
 PyDia_set_Array (Property *prop, PyObject *val)
 {
   ArrayProperty *p = (ArrayProperty *)prop;
-  guint i, num_props = p->ex_props->len;
-  PyDiaPropSetFunc *setters = g_new0(PyDiaPropSetFunc, num_props);
+  guint num_props = p->ex_props->len;
+  PyDiaPropSetFunc *setters = g_new0 (PyDiaPropSetFunc, num_props);
   int ret = 0;
 
   /* resolve the getter functions once */
-  for (i = 0; i < num_props; i++) {
-    Property *ex = g_ptr_array_index(p->ex_props, i);
-    int j;
-    for (j = 0; j < G_N_ELEMENTS(prop_type_map); j++) {
-      if (prop_type_map[j].quark == ex->type_quark)
-       setters[i] = (PyDiaPropSetFunc)prop_type_map[j].propset;
+  for (int i = 0; i < num_props; i++) {
+    Property *ex = g_ptr_array_index (p->ex_props, i);
+
+    for (int j = 0; j < G_N_ELEMENTS (prop_type_map); j++) {
+      if (prop_type_map[j].quark == ex->type_quark) {
+        setters[i] = (PyDiaPropSetFunc) prop_type_map[j].propset;
+      }
     }
+
     if (!setters[i]) {
       g_debug ("%s: No setter for '%s'", G_STRLOC, ex->descr->type);
       g_clear_pointer (&setters, g_free);
       return -1;
     }
   }
+
   /* tuple or list containing tuples */
-  if (PyTuple_Check(val) || PyList_Check(val)) {
-    gboolean is_list = !PyTuple_Check(val);
-    guint len = is_list ? PyList_Size(val) : PyTuple_Size(val);
-    for (i = 0; i < p->records->len; i++) {
-      GPtrArray *record = g_ptr_array_index(p->records, i);
-      guint j;
-      for (j = 0; j < num_props; j++) {
-       Property *inner =g_ptr_array_index(record,j);
-       inner->ops->free(inner);
+  if (PyTuple_Check (val) || PyList_Check (val)) {
+    gboolean is_list = !PyTuple_Check (val);
+    guint len = is_list ? PyList_Size (val) : PyTuple_Size (val);
+
+    for (int i = 0; i < p->records->len; i++) {
+      GPtrArray *record = g_ptr_array_index (p->records, i);
+
+      for (int j = 0; j < num_props; j++) {
+        Property *inner =g_ptr_array_index (record, j);
+        inner->ops->free (inner);
       }
-      g_ptr_array_free(record, TRUE);
+      g_ptr_array_free (record, TRUE);
     }
-    g_ptr_array_set_size(p->records, 0);
-    for (i = 0; i < len; i++) {
-      PyObject *t = is_list ? PyList_GetItem(val, i) : PyTuple_GetItem(val, i);
-      GPtrArray *record = g_ptr_array_new();
-      guint j;
-      if (!PyTuple_Check(t) || PyTuple_Size(t) != num_props) {
+
+    g_ptr_array_set_size (p->records, 0);
+
+    for (int i = 0; i < len; i++) {
+      PyObject *t = is_list ? PyList_GetItem (val, i) : PyTuple_GetItem (val, i);
+      GPtrArray *record = g_ptr_array_new ();
+
+      if (!PyTuple_Check (t) || PyTuple_Size (t) != num_props) {
         g_debug ("%s: PyDia_set_Array: %s.",
                  G_STRLOC,
-                 !PyTuple_Check(t) ? "no tuple" : " wrong size");
+                 !PyTuple_Check (t) ? "no tuple" : " wrong size");
         ret = -1;
         break;
       }
-      g_ptr_array_set_size(record, 0);
-      for (j = 0; j < num_props; j++) {
+
+      g_ptr_array_set_size (record, 0);
+
+      for (int j = 0; j < num_props; j++) {
         Property *ex = g_ptr_array_index (p->ex_props, j);
         Property *inner = ex->ops->copy (ex);
         PyObject *v = PyTuple_GetItem (t, j);
@@ -736,14 +941,20 @@ PyDia_set_Array (Property *prop, PyObject *val)
             break;
           }
         }
+
         g_ptr_array_add (record, inner);
       }
-      g_ptr_array_add(p->records, record);
-      if (ret != 0)
+
+      g_ptr_array_add (p->records, record);
+
+      if (ret != 0) {
         break;
+      }
     }
   }
+
   g_clear_pointer (&setters, g_free);
+
   return ret;
 }
 
@@ -758,8 +969,8 @@ _keyvalue_get (gpointer key,
   PyObject *dict = (PyObject *)user_data;
   PyObject *k, *v;
 
-  k = PyString_FromString (name);
-  v = PyString_FromString (val);
+  k = PyUnicode_FromString (name);
+  v = PyUnicode_FromString (val);
 
   if (k && v) {
     PyDict_SetItem (dict, k, v);
@@ -788,23 +999,23 @@ PyDia_set_Dict (Property *prop, PyObject *val)
 {
   DictProperty *p = (DictProperty *)prop;
 
-  if PyDict_Check(val) {
+  if (PyDict_Check (val)) {
     Py_ssize_t i = 0; /* not to be modified! */
     PyObject *key, *value;
 
-
     if (!p->dict) {
       p->dict = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
     }
 
-    while (PyDict_Next(val, &i, &key, &value)) {
+    while (PyDict_Next (val, &i, &key, &value)) {
       /* CHECK semantics: replace or add? */
-      g_hash_table_insert (p->dict, g_strdup (PyString_AsString (key)),
-      g_strdup (PyString_AsString (value)));
+      g_hash_table_insert (p->dict, g_strdup (PyUnicode_AsUTF8 (key)),
+      g_strdup (PyUnicode_AsUTF8 (value)));
     }
 
     return 0;
   }
+
   return -1;
 }
 
@@ -818,7 +1029,7 @@ PyDia_get_Pixbuf (PixbufProperty *prop)
     Py_INCREF(Py_None);
     return Py_None;
   }
-  pb = PyCObject_FromVoidPtrAndDesc (prop->pixbuf, NULL, NULL);
+  pb = PyCapsule_New (prop->pixbuf, "pixbuf", NULL);
 
   return pb;
 }
@@ -829,13 +1040,14 @@ PyDia_set_Pixbuf (Property *prop, PyObject *val)
 {
   PixbufProperty *p = (PixbufProperty *) prop;
 
-  if PyCObject_Check(val) {
-    gpointer pp = PyCObject_AsVoidPtr (val);
+  if (PyCapsule_IsValid (val, "pixbuf")) {
+    gpointer pp = PyCapsule_GetPointer (val, "pixbuf");
 
     /* FIXME: refernce counting? */
     p->pixbuf = pp;
     return 0;
   }
+
   return -1;
 }
 
@@ -844,42 +1056,55 @@ PyDia_set_Pixbuf (Property *prop, PyObject *val)
  * GetAttr
  */
 static PyObject *
-PyDiaProperty_GetAttr(PyDiaProperty *self, char *attr)
-{
-
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[ssss]", "name", "type", "value", "visible", "description", "tooltip");
-  else if (!strcmp(attr, "name"))
-    return PyString_FromString(self->property->descr->name);
-  else if (!strcmp(attr, "type"))
-    return PyString_FromString(self->property->descr->type);
-  else if (!strcmp(attr, "description"))
-    return PyString_FromString(self->property->descr->description);
-  else if (!strcmp(attr, "tooltip"))
-    return PyString_FromString(self->property->descr->tooltip);
-  else if (!strcmp(attr, "visible"))
-    return PyInt_FromLong(0 != (self->property->descr->flags & PROP_FLAG_VISIBLE));
-  else if (!strcmp(attr, "value")) {
+PyDiaProperty_GetAttr (PyObject *obj, PyObject *arg)
+{
+  PyDiaProperty *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaProperty *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ssss]",
+                          "name", "type", "value", "visible", "description",
+                          "tooltip");
+  } else if (!g_strcmp0 (attr, "name")) {
+    return PyUnicode_FromString (self->property->descr->name);
+  } else if (!g_strcmp0 (attr, "type")) {
+    return PyUnicode_FromString (self->property->descr->type);
+  } else if (!g_strcmp0 (attr, "description")) {
+    return PyUnicode_FromString (self->property->descr->description);
+  } else if (!g_strcmp0 (attr, "tooltip")) {
+    return PyUnicode_FromString (self->property->descr->tooltip);
+  } else if (!g_strcmp0 (attr, "visible")) {
+    return PyLong_FromLong (0 != (self->property->descr->flags & PROP_FLAG_VISIBLE));
+  } else if (!g_strcmp0 (attr, "value")) {
     int i;
 
-    ensure_quarks();
+    ensure_quarks ();
     for (i = 0; i < G_N_ELEMENTS (prop_type_map); i++) {
       if (prop_type_map[i].quark == self->property->type_quark) {
         return prop_type_map[i].propget (self->property);
       }
     }
+
     if (0 == (PROP_FLAG_WIDGET_ONLY & self->property->descr->flags)) {
       g_debug ("%s: No handler for type '%s'",
                G_STRLOC,
                self->property->descr->type);
     }
 
-    Py_INCREF(Py_None);
+    Py_INCREF (Py_None);
     return Py_None;
   }
 
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
 
@@ -887,25 +1112,26 @@ PyDiaProperty_GetAttr(PyDiaProperty *self, char *attr)
  * Similar to SetAttr but the property is directly applied
  * to the DiaObject
  */
-int PyDiaProperty_ApplyToObject (DiaObject *object,
-                                 char      *key,
-                                 Property  *prop,
-                                 PyObject  *val)
+int
+PyDiaProperty_ApplyToObject (DiaObject  *object,
+                             const char *key,
+                             Property   *prop,
+                             PyObject   *val)
 {
   int ret = -1;
 
-  if PyDiaProperty_Check(val) {
+  if (PyDiaProperty_Check (val)) {
     /* must be a Property object ? Or PyDiaRect etc ? */
-    Property* inprop = ((PyDiaProperty*)val)->property;
+    Property *inprop = ((PyDiaProperty *) val)->property;
 
-    if (0 == strcmp (prop->descr->type, inprop->descr->type)) {
+    if (g_strcmp0 (prop->descr->type, inprop->descr->type) == 0) {
       GPtrArray *plist;
       /* apply it */
       prop->ops->free (prop); /* release this one */
-      prop = inprop->ops->copy(inprop);
+      prop = inprop->ops->copy (inprop);
       /* apply property to object */
       plist = prop_list_from_single (prop);
-      object->ops->set_props(object, plist);
+      dia_object_set_properties (object, plist);
       prop_list_free (plist);
       return 0;
     } else {
@@ -916,14 +1142,14 @@ int PyDiaProperty_ApplyToObject (DiaObject *object,
     }
   } else {
     int i;
-    ensure_quarks();
-    for (i = 0; i < G_N_ELEMENTS(prop_type_map); i++) {
+    ensure_quarks ();
+    for (i = 0; i < G_N_ELEMENTS (prop_type_map); i++) {
       if (prop_type_map[i].quark == prop->type_quark) {
         if (!prop_type_map[i].propset) {
           g_debug ("%s: Setter for '%s' not implemented.",
                    G_STRLOC,
                    prop_type_map[i].type);
-        } else if (0 == prop_type_map[i].propset(prop, val)) {
+        } else if (0 == prop_type_map[i].propset (prop, val)) {
           ret = 0;
         }
         break;
@@ -940,7 +1166,7 @@ int PyDiaProperty_ApplyToObject (DiaObject *object,
   if (0 == ret) {
     /* apply property to object */
     GPtrArray *plist = prop_list_from_single (prop);
-    object->ops->set_props(object, plist);
+    dia_object_set_properties (object, plist);
     prop_list_free (plist);
   }
 
@@ -952,8 +1178,9 @@ int PyDiaProperty_ApplyToObject (DiaObject *object,
  * Repr / _Str
  */
 static PyObject *
-PyDiaProperty_Str (PyDiaProperty *self)
+PyDiaProperty_Str (PyObject *obj)
 {
+  PyDiaProperty *self = (PyDiaProperty *) obj;
   PyObject* py_s;
   char* s;
 
@@ -962,8 +1189,9 @@ PyDiaProperty_Str (PyDiaProperty *self)
                        self->property->descr->name,
                        self->property->descr->type);
 
-  py_s = PyString_FromString(s);
+  py_s = PyUnicode_FromString (s);
   g_clear_pointer (&s, g_free);
+
   return py_s;
 }
 
@@ -981,40 +1209,21 @@ static PyMemberDef PyDiaProperty_Members[] = {
       "bool: visibility of the property" },
     { NULL }
 };
+
+
 /*
  * Python object
  */
 PyTypeObject PyDiaProperty_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Property",
-    sizeof(PyDiaProperty),
-    0,
-    (destructor)PyDiaProperty_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaProperty_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaProperty_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaProperty_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaProperty_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "Interface to so called StdProps, the mechanism to control "
-    "most of Dia's canvas objects properties. ",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaProperty_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Property",
+  .tp_basicsize = sizeof (PyDiaProperty),
+  .tp_dealloc = PyDiaProperty_Dealloc,
+  .tp_getattro = PyDiaProperty_GetAttr,
+  .tp_richcompare = PyDiaProperty_RichCompare,
+  .tp_hash = PyDiaProperty_Hash,
+  .tp_str = PyDiaProperty_Str,
+  .tp_doc = "Interface to so called StdProps, the mechanism to control "
+            "most of Dia's canvas objects properties. ",
+  .tp_members = PyDiaProperty_Members,
 };
diff --git a/plug-ins/python/pydia-render.c b/plug-ins/python/pydia-render.c
index abe095020..b4392c89f 100644
--- a/plug-ins/python/pydia-render.c
+++ b/plug-ins/python/pydia-render.c
@@ -365,13 +365,13 @@ is_capable_to (DiaRenderer *renderer, RenderCapability cap)
 
   func = PyObject_GetAttrString (self, "is_capable_to");
   if (func && PyCallable_Check(func)) {
-    Py_INCREF(self);
-    Py_INCREF(func);
+    Py_INCREF (self);
+    Py_INCREF (func);
     arg = Py_BuildValue ("(i)", cap);
     if (arg) {
       res = PyEval_CallObject (func, arg);
-      if (res && PyInt_Check(res)) {
-        bRet = (PyInt_AsLong(res) != 0);
+      if (res && PyLong_Check (res)) {
+        bRet = (PyLong_AsLong (res) != 0);
         Py_DECREF (res);
       } else {
         ON_RES(res, FALSE);
@@ -412,7 +412,7 @@ draw_layer (DiaRenderer  *renderer,
     Py_INCREF (self);
     Py_INCREF (func);
     if (update) {
-      orect = PyDiaRectangle_New (update, NULL);
+      orect = PyDiaRectangle_New (update);
     } else {
       Py_INCREF(Py_None);
       orect = Py_None;
diff --git a/plug-ins/python/pydia-sheet.c b/plug-ins/python/pydia-sheet.c
index 9c3529d06..6f544f841 100644
--- a/plug-ins/python/pydia-sheet.c
+++ b/plug-ins/python/pydia-sheet.c
@@ -36,30 +36,34 @@ PyDiaSheet_New(Sheet *sheet)
     return (PyObject *)self;
 }
 
+
 static void
-PyDiaSheet_Dealloc(PyDiaSheet *self)
+PyDiaSheet_Dealloc (PyObject *self)
 {
-     PyObject_DEL(self);
+  PyObject_DEL (self);
 }
 
-static int
-PyDiaSheet_Compare(PyDiaSheet *self, PyDiaSheet *other)
+
+static PyObject *
+PyDiaSheet_RichCompare (PyObject *self, PyObject *other, int op)
 {
-    if (self->sheet == other->sheet) return 0;
-    if (self->sheet > other->sheet) return -1;
-    return 1;
+  Py_RETURN_RICHCOMPARE (((PyDiaSheet *) self)->sheet,
+                         ((PyDiaSheet *) other)->sheet,
+                         op);
 }
 
+
 static long
-PyDiaSheet_Hash(PyDiaSheet *self)
+PyDiaSheet_Hash (PyObject *self)
 {
-    return (long)self->sheet;
+  return (long) ((PyDiaSheet *) self)->sheet;
 }
 
+
 static PyObject *
-PyDiaSheet_Str(PyDiaSheet *self)
+PyDiaSheet_Str (PyObject *self)
 {
-    return PyString_FromString(self->sheet->description);
+  return PyUnicode_FromString (((PyDiaSheet *) self)->sheet->description);
 }
 
 /*
@@ -86,75 +90,76 @@ static PyMemberDef PyDiaSheet_Members[] = {
 };
 
 static PyObject *
-PyDiaSheet_GetAttr(PyDiaSheet *self, gchar *attr)
+PyDiaSheet_GetAttr (PyObject *obj, PyObject *arg)
 {
-    if (!strcmp(attr, "__members__"))
-       return Py_BuildValue("[ssss]", "name", "description", "filename", "objects");
-    else if (!strcmp(attr, "name"))
-       return PyString_FromString(self->sheet->name);
-    else if (!strcmp(attr, "description"))
-       return PyString_FromString(self->sheet->description);
-    else if (!strcmp(attr, "filename"))
-       return PyString_FromString(self->sheet->filename);
-    else if (!strcmp(attr, "user"))
-       return PyInt_FromLong(self->sheet->scope == SHEET_SCOPE_USER ? 1 : 0);
-    else if (!strcmp(attr, "objects")) {
-       /* Just returning tuples with information for now. Wrapping SheetObject
-        * looks like overkill for the time being.
-        *  - DiaObjectType or None
-        *  - description of the SheetObject
-        *  - filename of the icon file
-        */
-       PyObject *ret = PyList_New(0);
-       GSList *list;
-
-       for (list = self->sheet->objects; list != NULL; list = list->next) {
-           SheetObject *so = list->data;
-           DiaObjectType *ot = object_get_type (so->object_type);
-
-           if (!ot)
-               Py_INCREF(Py_None);
-           PyList_Append(ret, Py_BuildValue ("(Oss)",
-                                               ot ? PyDiaObjectType_New (ot) : Py_None,
-                                               PyString_FromString (so->description ? so->description : ""),
-                                               PyString_FromString (so->pixmap_file ? so->pixmap_file : 
"")));
-       }
-       return ret;
+  PyDiaSheet *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaSheet *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[ssss]",
+                          "name", "description", "filename", "objects");
+  } else if (!g_strcmp0 (attr, "name")) {
+    return PyUnicode_FromString (self->sheet->name);
+  } else if (!g_strcmp0 (attr, "description")) {
+    return PyUnicode_FromString (self->sheet->description);
+  } else if (!g_strcmp0 (attr, "filename")) {
+    return PyUnicode_FromString (self->sheet->filename);
+  } else if (!g_strcmp0 (attr, "user")) {
+    return PyLong_FromLong (self->sheet->scope == SHEET_SCOPE_USER ? 1 : 0);
+  } else if (!g_strcmp0 (attr, "objects")) {
+    /* Just returning tuples with information for now. Wrapping SheetObject
+    * looks like overkill for the time being.
+    *  - DiaObjectType or None
+    *  - description of the SheetObject
+    *  - filename of the icon file
+    */
+    PyObject *ret = PyList_New (0);
+    GSList *list;
+
+    for (list = self->sheet->objects; list != NULL; list = list->next) {
+      SheetObject *so = list->data;
+      DiaObjectType *ot = object_get_type (so->object_type);
+      PyObject *py_ot;
+      PyObject *val;
+
+      if (ot) {
+        py_ot = PyDiaObjectType_New (ot);
+      } else {
+        py_ot = Py_None;
+        Py_INCREF (Py_None);
+      }
+
+      val = Py_BuildValue ("(Oss)", py_ot, so->description, so->pixmap_file);
+
+      PyList_Append (ret, val);
     }
 
-    return Py_FindMethod(PyDiaSheet_Methods, (PyObject *)self, attr);
+    return ret;
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 PyTypeObject PyDiaSheet_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Sheet",
-    sizeof(PyDiaSheet),
-    0,
-    (destructor)PyDiaSheet_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaSheet_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaSheet_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaSheet_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaSheet_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "returned by dia.register_export() but not used otherwise yet.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    PyDiaSheet_Methods, /* tp_methods */
-    PyDiaSheet_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Sheet",
+  .tp_basicsize = sizeof (PyDiaSheet),
+  .tp_dealloc = PyDiaSheet_Dealloc,
+  .tp_getattro = PyDiaSheet_GetAttr,
+  .tp_richcompare = PyDiaSheet_RichCompare,
+  .tp_hash = PyDiaSheet_Hash,
+  .tp_str = PyDiaSheet_Str,
+  .tp_doc = "returned by dia.register_export() but not used otherwise yet.",
+  .tp_methods = PyDiaSheet_Methods,
+  .tp_members = PyDiaSheet_Members,
 };
diff --git a/plug-ins/python/pydia-text.c b/plug-ins/python/pydia-text.c
index 56d1fe7d5..fee2394ed 100644
--- a/plug-ins/python/pydia-text.c
+++ b/plug-ins/python/pydia-text.c
@@ -52,76 +52,124 @@ PyDiaText_New (char *text_data, TextAttributes *attr)
  * Dealloc
  */
 static void
-PyDiaText_Dealloc(PyDiaText *self)
+PyDiaText_Dealloc (PyObject *self)
 {
-  g_clear_pointer (&self->text_data, g_free);
-  PyObject_DEL(self);
+  g_clear_pointer (&((PyDiaText *) self)->text_data, g_free);
+  PyObject_DEL (self);
 }
 
+
 /*
  * Compare
  */
-static int
-PyDiaText_Compare(PyDiaText *self,
-                  PyDiaText *other)
+static PyObject *
+PyDiaText_RichCompare (PyObject *a,
+                       PyObject *b,
+                       int       op)
 {
-  int i;
-  i = strcmp (self->text_data, other->text_data);
-  if (i != 0)
-    return i;
-  return memcmp(&(self->attr), &(other->attr), sizeof(TextAttributes));
+  PyDiaText *self = (PyDiaText *) a;
+  PyDiaText *other = (PyDiaText *) b;
+  int text_cmp = strcmp (self->text_data, other->text_data);
+  int attr_cmp = memcmp (&(self->attr), &(other->attr), sizeof (TextAttributes));
+  PyObject *ret;
+
+  switch (op) {
+    case Py_EQ:
+      ret = (text_cmp == 0 && attr_cmp == 0) ? Py_True : Py_False;
+      break;
+    case Py_NE:
+      ret = (text_cmp != 0 || attr_cmp != 0) ? Py_True : Py_False;
+      break;
+    case Py_LE:
+      ret = (text_cmp <= 0 && attr_cmp <= 0) ? Py_True : Py_False;
+      break;
+    case Py_GE:
+      ret = (text_cmp >= 0 && attr_cmp >= 0) ? Py_True : Py_False;
+      break;
+    case Py_LT:
+      ret = (text_cmp < 0 && attr_cmp < 0) ? Py_True : Py_False;
+      break;
+    case Py_GT:
+      ret = (text_cmp > 0 && attr_cmp > 0) ? Py_True : Py_False;
+      break;
+    default:
+      ret = Py_NotImplemented;
+      break;
+  }
+
+  Py_INCREF (ret);
+
+  return ret;
 }
 
+
 /*
  * Hash
  */
 static long
-PyDiaText_Hash(PyObject *self)
+PyDiaText_Hash (PyObject *self)
 {
-  return (long)self;
+  return (long) self;
 }
 
 /*
  * GetAttr
  */
 static PyObject *
-PyDiaText_GetAttr(PyDiaText *self, char *attr)
+PyDiaText_GetAttr (PyObject *obj, PyObject *arg)
 {
-  if (!strcmp(attr, "__members__"))
-    return Py_BuildValue("[sssss]", "text", "font", "height",
-                         "position", "color", "alignment");
-  else if (!strcmp(attr, "text"))
-    return PyString_FromString(self->text_data);
-  else if (!strcmp(attr, "font"))
-    return PyDiaFont_New(self->attr.font);
-  else if (!strcmp(attr, "height"))
-    return PyFloat_FromDouble(self->attr.height);
-  else if (!strcmp(attr, "position"))
-    return PyDiaPoint_New(&self->attr.position);
-  else if (!strcmp(attr, "color"))
-    return PyDiaColor_New(&self->attr.color);
-  else if (!strcmp(attr, "alignment"))
-    return PyInt_FromLong(self->attr.alignment);
-
-  PyErr_SetString(PyExc_AttributeError, attr);
-  return NULL;
+  PyDiaText *self;
+  const char *attr;
+
+  if (PyUnicode_Check (arg)) {
+    attr = PyUnicode_AsUTF8 (arg);
+  } else {
+    goto generic;
+  }
+
+  self = (PyDiaText *) obj;
+
+  if (!g_strcmp0 (attr, "__members__")) {
+    return Py_BuildValue ("[sssss]",
+                          "text", "font", "height", "position", "color",
+                          "alignment");
+  } else if (!g_strcmp0 (attr, "text")) {
+    return PyUnicode_FromString (self->text_data);
+  } else if (!g_strcmp0 (attr, "font")) {
+    return PyDiaFont_New (self->attr.font);
+  } else if (!g_strcmp0 (attr, "height")) {
+    return PyFloat_FromDouble (self->attr.height);
+  } else if (!g_strcmp0 (attr, "position")) {
+    return PyDiaPoint_New (&self->attr.position);
+  } else if (!g_strcmp0 (attr, "color")) {
+    return PyDiaColor_New (&self->attr.color);
+  } else if (!g_strcmp0 (attr, "alignment")) {
+    return PyLong_FromLong (self->attr.alignment);
+  }
+
+generic:
+  return PyObject_GenericGetAttr (obj, arg);
 }
 
+
 /*
  * Repr / _Str
  */
 static PyObject *
-PyDiaText_Str(PyDiaText *self)
+PyDiaText_Str (PyObject *self)
 {
   char *strname = g_strdup_printf ("<DiaText \"%s\" at %lx>",
-                                   self->attr.font ? dia_font_get_family (self->attr.font) : "none",
-                                   (long)self);
-  PyObject *ret = PyString_FromString(strname);
+                                   ((PyDiaText *) self)->attr.font ?
+                                      dia_font_get_family (((PyDiaText *) self)->attr.font) : "none",
+                                   (long) self);
+  PyObject *ret = PyUnicode_FromString (strname);
 
   g_clear_pointer (&strname, g_free);
+
   return ret;
 }
 
+
 #define T_INVALID -1 /* can't allow direct access due to pyobject->text indirection */
 static PyMemberDef PyDiaText_Members[] = {
     { "text", T_INVALID, 0, RESTRICTED|READONLY,
@@ -138,39 +186,21 @@ static PyMemberDef PyDiaText_Members[] = {
       "int: alignment out of LEFT=0, CENTER=1, RIGHT=2" },
     { NULL }
 };
+
+
 /*
  * Python objetcs
  */
 PyTypeObject PyDiaText_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,
-    "dia.Text",
-    sizeof(PyDiaText),
-    0,
-    (destructor)PyDiaText_Dealloc,
-    (printfunc)0,
-    (getattrfunc)PyDiaText_GetAttr,
-    (setattrfunc)0,
-    (cmpfunc)PyDiaText_Compare,
-    (reprfunc)0,
-    0,
-    0,
-    0,
-    (hashfunc)PyDiaText_Hash,
-    (ternaryfunc)0,
-    (reprfunc)PyDiaText_Str,
-    (getattrofunc)0,
-    (setattrofunc)0,
-    (PyBufferProcs *)0,
-    0L, /* Flags */
-    "Many objects (dia.Object) having text to display provide this property.",
-    (traverseproc)0,
-    (inquiry)0,
-    (richcmpfunc)0,
-    0, /* tp_weakliszoffset */
-    (getiterfunc)0,
-    (iternextfunc)0,
-    0, /* tp_methods */
-    PyDiaText_Members, /* tp_members */
-    0
+  PyVarObject_HEAD_INIT (NULL, 0)
+  .tp_name = "dia.Text",
+  .tp_basicsize = sizeof (PyDiaText),
+  .tp_dealloc = PyDiaText_Dealloc,
+  .tp_getattro = PyDiaText_GetAttr,
+  .tp_richcompare = PyDiaText_RichCompare,
+  .tp_hash = PyDiaText_Hash,
+  .tp_str = PyDiaText_Str,
+  .tp_doc = "Many objects (dia.Object) having text to display provide this "
+            "property.",
+  .tp_members = PyDiaText_Members,
 };
diff --git a/plug-ins/python/pydia.h b/plug-ins/python/pydia.h
new file mode 100644
index 000000000..abf3b9706
--- /dev/null
+++ b/plug-ins/python/pydia.h
@@ -0,0 +1,33 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright © 2020 Zander Brown <zbrown gnome org>
+ */
+
+#pragma once
+
+#include <glib.h>
+
+#include <Python.h>
+
+G_BEGIN_DECLS
+
+PyMODINIT_FUNC PyInit_dia (void);
+
+G_END_DECLS
diff --git a/plug-ins/python/pydiadoc.py b/plug-ins/python/pydiadoc.py
index ad1958bb8..7a1213d62 100644
--- a/plug-ins/python/pydiadoc.py
+++ b/plug-ins/python/pydiadoc.py
@@ -20,7 +20,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import sys, math, dia, types, string
+import sys, math, dia, types
 
 import gettext
 _ = gettext.gettext
@@ -103,10 +103,10 @@ def autodoc_cb (data, flags, update) :
        for s in ["Standard - Image", "Standard - BezierLine", "Standard - Text",
                "UML - Class", "UML - Dependency"] :
                o, h1, h2 = dia.get_object_type(s).create(0,0)
-               for p in o.properties.keys() :
+               for p in list(o.properties.keys()) :
                        v = o.properties[p].value
                        theObjects.append(v)
-                       if type(v) is types.TupleType and len(v) > 0 :
+                       if type(v) is tuple and len(v) > 0 :
                                theObjects.append(v[0])
                if once :
                        theObjects.append(o)
@@ -123,7 +123,7 @@ def autodoc_cb (data, flags, update) :
        for s in theDir :
                if s == "_dia" :
                        continue # avoid all the messy details ;)
-               if theTypes.has_key(s) :
+               if s in theTypes :
                        continue
                for o in theObjects :
                        is_a = eval("type(o) is dia." + s)
@@ -131,7 +131,7 @@ def autodoc_cb (data, flags, update) :
                        if is_a :
                                theTypes[s] = o
                                break
-               if not theTypes.has_key (s) :
+               if s not in theTypes :
                        theTypes[s] = eval ("dia." + s)
        # add UML classes for every object in dir
        #print theTypes
@@ -165,7 +165,7 @@ def autodoc_cb (data, flags, update) :
                # set the objects name
                o.properties["name"] = s
                # now populate the object with ...
-               if theTypes.has_key(s) :
+               if s in theTypes :
                        t = theTypes[s]
                        # ... methods and ...
                        methods = []
@@ -185,7 +185,7 @@ def autodoc_cb (data, flags, update) :
                                try :
                                        is_m = eval("callable(t." + m + ")")
                                except :
-                                       print "type(t." + m + ")?"
+                                       print("type(t." + m + ")?")
                                        is_m = 0
                                doc = ""
                                tt = ""
@@ -196,10 +196,10 @@ def autodoc_cb (data, flags, update) :
                                                        tt = eval("oo." + m + "().__class__.__name__")
                                                else :
                                                        tt = eval("t." + m + ".__class__.__name__")
-                                       except TypeError, msg :
-                                               print m, msg
-                                       except AttributeError, msg :
-                                               print m, msg # No constructor defined
+                                       except TypeError as msg :
+                                               print(m, msg)
+                                       except AttributeError as msg :
+                                               print(m, msg) # No constructor defined
                                try :
                                        # try to get the member's docstring from the type
                                        doc = eval("dia." + s + "." + m + ".__doc__")
@@ -234,7 +234,7 @@ def autodoc_cb (data, flags, update) :
        o.properties["visible_comments"] = show_comments
        methods = []
        for s in theGlobals :
-               if string.find(s[0], "swigregister") >= 0 :
+               if s[0].find("swigregister") >= 0 :
                        continue # just noise
                methods.append((s[0],'',s[1],'',0,0,0,1,()))
        o.properties["operations"] = methods
diff --git a/plug-ins/python/python-startup.py b/plug-ins/python/python-startup.py
index 9883724be..e9b9dad67 100644
--- a/plug-ins/python/python-startup.py
+++ b/plug-ins/python/python-startup.py
@@ -20,7 +20,7 @@ def load(plugindir):
                                        sys.stderr.write('could not import %s [%s]\n' % (file, e))
 
 # import any python plugins from the user ...
-if not os.environ.has_key('HOME'):
+if 'HOME' not in os.environ:
        os.environ['HOME'] = os.pathsep + 'tmp'
 # import all plugins found in user plugin dir
 load(os.path.join(os.environ['HOME'], '.dia', 'python'))
diff --git a/plug-ins/python/python.c b/plug-ins/python/python.c
index 2b0666240..66f6b55b5 100644
--- a/plug-ins/python/python.c
+++ b/plug-ins/python/python.c
@@ -27,6 +27,7 @@
 #include "plug-ins.h"
 #include "dia_dirs.h"
 
+#include "pydia.h"
 #include "pydia-error.h"
 
 DIA_PLUGIN_CHECK_INIT
@@ -81,7 +82,7 @@ dia_py_plugin_unload (PluginInfo *info)
 PluginInitResult
 dia_plugin_init (PluginInfo *info)
 {
-  char *python_argv[] = { "dia-python", NULL };
+  wchar_t *python_argv[] = { L"dia-python", NULL };
   char *startup_file;
   FILE *fp;
   PyObject *__main__, *__file__;
@@ -99,18 +100,16 @@ dia_plugin_init (PluginInfo *info)
     return DIA_PLUGIN_INIT_ERROR;
   }
 
-  Py_SetProgramName ("dia");
+  Py_SetProgramName (L"dia");
+
+  PyImport_AppendInittab ("dia", &PyInit_dia);
+
   Py_Initialize ();
 
   PySys_SetArgv (1, python_argv);
-  /* Sanitize sys.path */
-  PyRun_SimpleString ("import sys; sys.path = filter(None, sys.path)");
-
-  if (on_error_report()) {
-    return DIA_PLUGIN_INIT_ERROR;
-  }
 
-  initdia();
+  /* Sanitize sys.path */
+  PyRun_SimpleString ("import sys; sys.path = list(filter(None, sys.path))");
 
   if (on_error_report()) {
     return DIA_PLUGIN_INIT_ERROR;
@@ -132,7 +131,7 @@ dia_plugin_init (PluginInfo *info)
 
   /* set __file__ in __main__ so that python-startup.py knows where it is */
   __main__ = PyImport_AddModule ("__main__");
-  __file__ = PyString_FromString (startup_file);
+  __file__ = PyUnicode_FromString (startup_file);
   PyObject_SetAttrString (__main__, "__file__", __file__);
   Py_DECREF (__file__);
 #if defined(G_OS_WIN32) && (PY_VERSION_HEX >= 0x02040000)
diff --git a/plug-ins/python/scascale.py b/plug-ins/python/scascale.py
index e73859d43..021bdfd24 100644
--- a/plug-ins/python/scascale.py
+++ b/plug-ins/python/scascale.py
@@ -30,43 +30,45 @@ _ = gettext.gettext
 
 class CScaleDialog :
        def __init__(self, data) :
-               import pygtk
-               pygtk.require("2.0")
-               import gtk
-               win = gtk.Window()
+               import gi
+
+               gi.require_version('Gtk', '2.0')
+
+               from gi.repository import Gtk
+               win = Gtk.Window()
                win.connect("delete_event", self.on_delete)
                win.set_title(_("Simple Scaling"))
 
                self.diagram = data
                self.win = win
 
-               box1 = gtk.VBox()
+               box1 = Gtk.VBox()
                win.add(box1)
                box1.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2)
+               box1.pack_start(box2, True, True, 0)
                box2.show()
 
-               self.entry = gtk.Entry()
+               self.entry = Gtk.Entry()
                self.entry.set_text("0.1")
-               box2.pack_start(self.entry)
+               box2.pack_start(self.entry, True, True, 0)
                self.entry.show()
 
-               separator = gtk.HSeparator()
-               box1.pack_start(separator, expand=0)
+               separator = Gtk.HSeparator()
+               box1.pack_start(separator, 0, True, 0)
                separator.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2, expand=0)
+               box1.pack_start(box2, 0, True, 0)
                box2.show()
 
-               button = gtk.Button(_("Scale"))
+               button = Gtk.Button(_("Scale"))
                button.connect("clicked", self.on_scale)
-               box2.pack_start(button)
-               button.set_flags(gtk.CAN_DEFAULT)
+               box2.pack_start(button, True, True, 0)
+               button.set_can_default(True)
                button.grab_default()
                button.show()
                win.show()
@@ -86,9 +88,9 @@ class CScaleDialog :
                self.win.destroy ()
 
 def ScaleLens(o, factor) :
-       if o.properties.has_key("line_width") :
+       if "line_width" in o.properties :
                o.properties["line_width"] = o.properties["line_width"].value * factor
-       if o.properties.has_key("text_height") :
+       if "text_height" in o.properties :
                o.properties["text_height"] = o.properties["text_height"].value * factor
 
 def SimpleScale(data, factor) :
@@ -99,7 +101,7 @@ def SimpleScale(data, factor) :
        for o in objs :
                pos = o.properties["obj_pos"].value
                hSE = None # the 'south east' handle to size the object
-               if o.properties.has_key("elem_width") :
+               if "elem_width" in o.properties :
                        hLR = o.handles[7] # HANDLE_RESIZE_SE
                        try :
                                #if 0 == hLR.type : # HANDLE_NON_MOVABLE
@@ -112,8 +114,8 @@ def SimpleScale(data, factor) :
                                o.move(x, y)
                                o.move_handle(hLR, (x2, y2), 0, 0)
                                ScaleLens(o, factor)
-                       except RuntimeError, msg :
-                               if scaleFailed.has_key(o.type.name) :
+                       except RuntimeError as msg :
+                               if o.type.name in scaleFailed :
                                        scaleFailed[o.type.name] += 1
                                else :
                                        scaleFailed[o.type.name] = 1
@@ -132,14 +134,14 @@ def SimpleScale(data, factor) :
                                for h in handles :
                                        o.move_handle(h[0], h[1],  0, 0)
                                ScaleLens(o, factor)
-                       except RuntimeError, msg :
-                               if scaleFailed.has_key(o.type.name) :
+                       except RuntimeError as msg :
+                               if o.type.name in scaleFailed :
                                        scaleFailed[o.type.name] += 1
                                else :
                                        scaleFailed[o.type.name] = 1
-       if len(scaleFailed.keys()) > 0 :
+       if len(list(scaleFailed.keys())) > 0 :
                sMsg = "Scaling failed for : "
-               for s in scaleFailed.keys() :
+               for s in list(scaleFailed.keys()) :
                        sMsg = sMsg + "\n%s (%d)" % (s, scaleFailed[s])
                dia.message(1, sMsg)
        data.update_extents ()
diff --git a/plug-ins/python/select_by.py b/plug-ins/python/select_by.py
index 2ba5c68b4..85d00f5a9 100644
--- a/plug-ins/python/select_by.py
+++ b/plug-ins/python/select_by.py
@@ -29,44 +29,46 @@ _ = gettext.gettext
 
 class CFindDialog :
        def __init__(self, data) :
-               import pygtk
-               pygtk.require("2.0")
-               import gtk
+               import gi
 
-               win = gtk.Window()
+               gi.require_version('Gtk', '2.0')
+
+               from gi.repository import Gtk
+
+               win = Gtk.Window()
                win.connect("delete_event", self.on_delete)
                win.set_title(_("Select by name"))
 
                self.diagram = data
                self.win = win
 
-               box1 = gtk.VBox()
+               box1 = Gtk.VBox()
                win.add(box1)
                box1.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2)
+               box1.pack_start(box2, True, True, 0)
                box2.show()
 
-               self.entry = gtk.Entry()
+               self.entry = Gtk.Entry()
                self.entry.set_text(_("Enter name"))
-               box2.pack_start(self.entry)
+               box2.pack_start(self.entry, True, True, 0)
                self.entry.show()
 
-               separator = gtk.HSeparator()
-               box1.pack_start(separator, expand=0)
+               separator = Gtk.HSeparator()
+               box1.pack_start(separator, 0, True, 0)
                separator.show()
 
-               box2 = gtk.VBox(spacing=10)
+               box2 = Gtk.VBox(spacing=10)
                box2.set_border_width(10)
-               box1.pack_start(box2, expand=0)
+               box1.pack_start(box2, 0, True, 0)
                box2.show()
 
-               button = gtk.Button(_("Find"))
+               button = Gtk.Button(_("Find"))
                button.connect("clicked", self.on_find)
-               box2.pack_start(button)
-               button.set_flags(gtk.CAN_DEFAULT)
+               box2.pack_start(button, True, True, 0)
+               button.set_can_default(True)
                button.grab_default()
                button.show()
                win.show()
@@ -87,8 +89,8 @@ def do_dialog(data):
 def select_by (diagram, name, value) :
        objs = diagram.active_layer.objects
        for o in objs :
-               if o.properties.has_key (name) :
-                       print "<==", o.properties[name].value
+               if name in o.properties :
+                       print("<==", o.properties[name].value)
                        if value == None or o.properties[name].value == value :
                                diagram.select (o)
 
@@ -99,7 +101,7 @@ def select_by_selected (data, name) :
        grp = data.get_sorted_selected()
        bFoundAny = 0
        for o in grp :
-               if o.properties.has_key(name) :
+               if name in o.properties :
                        select_by (data, name, o.properties[name].value)
                        bFoundAny = 1
        if not bFoundAny :
diff --git a/plug-ins/python/uninline_data.py b/plug-ins/python/uninline_data.py
index 0101ab991..5737a65ee 100644
--- a/plug-ins/python/uninline_data.py
+++ b/plug-ins/python/uninline_data.py
@@ -28,10 +28,10 @@ class UninlineRenderer :
                imgmap = {}
                dirname = os.path.dirname (filename)
                basename = os.path.basename(filename)
-               ext = filename[string.rfind(filename, ".")+1:]
+               ext = filename[filename.rfind(".")+1:]
                for layer in data.layers :
                        for o in layer.objects :
-                               if "inline_data" in o.properties.keys() :
+                               if "inline_data" in list(o.properties.keys()) :
                                        if o.properties["inline_data"].value :
                                                # remember by position
                                                pos = o.properties["obj_pos"].value
@@ -39,9 +39,9 @@ class UninlineRenderer :
                                                yk = "%03g" % pos.y
                                                key = basename + "-" + layer.name + "x" + xk + "y" + yk
                                                imgmap[key] = o
-                       for k, o in imgmap.iteritems() :
+                       for k, o in imgmap.items() :
                                fname = dirname + "/" + k + "." + ext
-                               print fname
+                               print(fname)
                                o.properties["image_file"] = fname
                                o.properties["inline_data"] = 0
 
diff --git a/plug-ins/python/wdeps.py b/plug-ins/python/wdeps.py
index b3e89c6d0..f120c5e1b 100644
--- a/plug-ins/python/wdeps.py
+++ b/plug-ins/python/wdeps.py
@@ -15,7 +15,7 @@
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import sys, os, re, string, math, time
+import sys, os, re, math, time
 
 g_maxWeight = 1
 # a very limited demangling (leaves junk for some forms)
@@ -26,11 +26,11 @@ g_maxWeight = 1
 # 3) other members have '' following function@class_and_namespaces@@parameters
 
 # this is overly simplified: somehow the key appears to have more than one meaning
-Operators = { "2" : " new", "3" : " delete", 
-                         "4" : "=", "5" : ">>", "6" : "<<", "7" : "!", "8" : "==", "9" : "!=", 
-                         "C" : "->", "Z" : "-", "F" : "--", "G" : "-", "H" : "+", "D" : "*", "E" : "++", 
+Operators = { "2" : " new", "3" : " delete",
+                         "4" : "=", "5" : ">>", "6" : "<<", "7" : "!", "8" : "==", "9" : "!=",
+                         "C" : "->", "Z" : "-", "F" : "--", "G" : "-", "H" : "+", "D" : "*", "E" : "++",
                          "M" : "<", "Y" : "+=", "A" : "[]", "B" : " char const *", "K" : "/" }
-rDemangle = re.compile ("\?(?P<tor>\?[01" + string.join(Operators.keys(), "") + 
"])*(?P<sym>[\w@_]+?(?=@))@@")
+rDemangle = re.compile ("\?(?P<tor>\?[01" + "".join(list(Operators.keys())) + "])*(?P<sym>[\w@_]+?(?=@))@@")
 # still NOT handling
 '''
 d:\Projects>undname ?ReleaseAccess@?$CWriteAccess@VCDocEx@app@@@utl@@UAEXXZ
@@ -56,17 +56,17 @@ def Demangle (symbols) :
                if m :
                        #print m.group("tor"), "::", m.group("sym")
                        dm = ""
-                       names = string.split (m.group("sym"), "@")
+                       names = m.group("sym").split ("@")
                        # we must not append the .reverse() in the line above otherwise names would become 
None. WTF? Inplace operation?
                        names.reverse()
                        if m.group("tor") == "?0" : # constructor
-                               dm =  string.join(names, "::") + "::" + names[-1]
+                               dm = "::".join(names) + "::" + names[-1]
                        elif  m.group("tor") == "?1" : # constructor
-                               dm =  string.join(names, "::") + "::~" + names[-1]
-                       elif  m.group("tor") != None and m.group("tor")[0] == "?" and m.group("tor")[1] in 
Operators.keys() : # constructor
-                               dm =  string.join(names, "::") + "::operator" + Operators[m.group("tor")[1]]
+                               dm = "::".join(names) + "::~" + names[-1]
+                       elif  m.group("tor") != None and m.group("tor")[0] == "?" and m.group("tor")[1] in 
list(Operators.keys()) : # constructor
+                               dm = "::".join(names) + "::operator" + Operators[m.group("tor")[1]]
                        else :
-                               dm =  string.join(names, "::")
+                               dm = "::".join(names)
                        #print " => ", dm
                        demangled.append (dm)
                else :
@@ -86,7 +86,7 @@ class Node :
        def AddExports (self, symbols, used=0) :
                ''' remember the symbols this node exports (and their use)'''
                for s in symbols :
-                       if self.exports.has_key(s) :
+                       if s in self.exports :
                                self.exports[s] += used
                        else :
                                self.exports[s] = used
@@ -113,7 +113,7 @@ def FindInPath (sName) :
        myPath = os.environ['PATH']
        if g_path :
                myPath = g_path
-       for sDir in string.split (myPath, os.pathsep) :
+       for sDir in myPath.split (os.pathsep) :
                p = sDir + os.sep + sName
                if os.path.exists (p) :
                        return p
@@ -126,11 +126,11 @@ def GetDepsDotnet (sFrom, dAll, node, nMaxDepth, nDepth=0) :
                import clr
                clr.AddReference("IronPython")
        except ImportError :
-               print "No Iron!"
+               print("No Iron!")
                return
        from System.Reflection import Assembly
 
-       if not dAll.has_key (sFrom) or node :
+       if sFrom not in dAll or node :
                if not node :
                        node = Node (sFrom, nDepth)
                        dAll[sFrom] = node
@@ -143,7 +143,7 @@ def GetDepsDotnet (sFrom, dAll, node, nMaxDepth, nDepth=0) :
                except SystemError :
                        return
                symbols = []
-               
+
                try :
                        types = assembly.GetExportedTypes()
                except IOError :
@@ -166,7 +166,7 @@ def GetDepsDotnet (sFrom, dAll, node, nMaxDepth, nDepth=0) :
                refs = assembly.GetReferencedAssemblies()
                for r in refs :
                        n2 = r.Name + ".dll";
-                       print "\t" * nDepth + n2, r.Version
+                       print("\t" * nDepth + n2, r.Version)
                        symbols = [ "[" + str(r.Version) + "]" ]
                        node.AddEdge (n2, symbols, 1) # delayLoad: kind of
                        GetDepsDotnet (n2, dAll, None, nMaxDepth, nDepth+1)
@@ -175,12 +175,12 @@ def GetDepsWin32 (sFrom, dAll, nMaxDepth, nDepth=0) :
        "calculates the dependents of the passed in dll"
        if nMaxDepth <= nDepth :
                return
-       sFrom = string.lower(sFrom)
+       sFrom = sFrom.lower()
        global g_DontFollow
        if sFrom in g_DontFollow :
                dAll[sFrom] = Node (sFrom, nDepth) # needed here so other algoritm can remove it ;)
                return
-       if not dAll.has_key (sFrom) :
+       if sFrom not in dAll :
                node = Node (sFrom, nDepth)
                sPath = sFrom
                if g_path :
@@ -213,7 +213,7 @@ def GetDepsWin32 (sFrom, dAll, nMaxDepth, nDepth=0) :
                        if s.find ("delay load imports") > 0 :
                                delayLoad = 1
                        if r :
-                               name = string.lower(r.group(1))
+                               name = r.group(1).lower()
                                # print name
                        else :
                                # import by name
@@ -234,17 +234,17 @@ def GetDepsWin32 (sFrom, dAll, nMaxDepth, nDepth=0) :
 
                # check for dotnet dependencies
                if "mscoree.dll" in directDeps :
-                       print "Dotnet check ...", sFrom
+                       print("Dotnet check ...", sFrom)
                        GetDepsDotnet (sFrom, dAll, node, nMaxDepth-nDepth+2, nDepth+1)
 
                # restore original depth (independent of how the recursion works)
                for sd in directDeps :
-                       if sd in dAll.keys() :
+                       if sd in list(dAll.keys()) :
                                try :
-                                       dAll[sd].depth = nDepth + 1 
-                               except AttributeError, msg :
-                                       print "FIXME:", sd, msg
-       
+                                       dAll[sd].depth = nDepth + 1
+                               except AttributeError as msg :
+                                       print("FIXME:", sd, msg)
+
 def GetDepsPosix (sFrom, dAll, nMaxDepth, nDepth=0) :
        "calculates the dependents of the passed in so"
        # sFrom must be with absolute on Posix, still we prefer the shorter name
@@ -255,8 +255,8 @@ def GetDepsPosix (sFrom, dAll, nMaxDepth, nDepth=0) :
        if sFromName in g_DontFollow :
                dAll[sFromName] = Node (sFromName, nDepth) # needed here so other algoritm can remove it ;)
                return
-       if not sFromName in dAll.keys() :
-               print "Creating", sFromName, nDepth
+       if not sFromName in list(dAll.keys()) :
+               print("Creating", sFromName, nDepth)
                node = Node (sFromName, nDepth)
                sPath = sFrom
                #TODO: work with relative pathes? Current dir?
@@ -286,7 +286,7 @@ def GetDepsPosix (sFrom, dAll, nMaxDepth, nDepth=0) :
                        else :
                                mi = re.match("\s+U (\S+)", si)
                                if mi :
-                                       print mi.group(1)
+                                       print(mi.group(1))
                                        dImports[mi.group(1)] = 1
                toRecurse = []
                for s in sModules :
@@ -303,19 +303,19 @@ def GetDepsPosix (sFrom, dAll, nMaxDepth, nDepth=0) :
                                        fExports = os.popen ('nm -g -U ' + r.group("path"))
                                else :
                                        fExports = os.popen ('nm --extern-only --dynamic --defined-only ' + 
r.group("path"))
-                               sExports = fExports.readlines() 
+                               sExports = fExports.readlines()
                                for se in sExports :
                                        me = re.match ("\w+ T (\S+)", se)
                                        if me :
                                                symbol = me.group(1)
                                                # print symbol
-                                               # print me.group(1), r.group('name') 
-                                               if symbol in dImports.keys() :
+                                               # print me.group(1), r.group('name')
+                                               if symbol in list(dImports.keys()) :
                                                        # direct connection
                                                        usedSymbols.append (symbol)
                                                else :
                                                        unusedSymbols.append(symbol)
-                               # now we have complete symbols for node r.group('name'), 
+                               # now we have complete symbols for node r.group('name'),
                                # should remember the node although it may not be connected yet
                                # OTOH the recursion would become more difficult to understand, favor KISS
                                if len(usedSymbols) > 0 :
@@ -325,14 +325,14 @@ def GetDepsPosix (sFrom, dAll, nMaxDepth, nDepth=0) :
                                                sToPath = r.group("path")
                                                node.AddEdge(sToPath[sToPath.rfind('/')+1:], usedSymbols, 0)
                                        toRecurse.append(r.group("path"))
-                                       print sFromName, "(", nDepth, "):", r.group("path")
+                                       print(sFromName, "(", nDepth, "):", r.group("path"))
                # add to all nodes
                dAll[sFromName] = node
                for sd in toRecurse :
-                       print sFromName, "->", sd, "level:", nDepth+1
+                       print(sFromName, "->", sd, "level:", nDepth+1)
                        sdn = os.path.split(sd)[-1]
                        GetDepsPosix (sd, dAll, nMaxDepth-nDepth+2, nDepth+1)
-                       if sdn in dAll.keys() :
+                       if sdn in list(dAll.keys()) :
                                #print "Adjusting depth", sd, nDepth+1
                                dAll[sdn].depth = nDepth+1 # adjust to original depth (not how our recursion 
found it)
 
@@ -342,42 +342,42 @@ def IsWin32 () :
 def Remove (deps, list) :
        "From the deps tree remove the nodes matching list"
        for s in list :
-               if deps.has_key (s) :
+               if s in deps :
                        del deps[s]
-               for sn in deps.keys() :
+               for sn in list(deps.keys()) :
                        node = deps[sn]
-                       if node.deps.has_key (s) :
+                       if s in node.deps :
                                del node.deps[s]
 def RemoveRegEx (deps, reg_ex) :
        "If a module matches reg_ex remove it"
        import re
        rr = re.compile (reg_ex)
-       for k1 in deps.keys() :
+       for k1 in list(deps.keys()) :
                if rr.match (k1) :
                        del deps[k1]
                else :
                        node = deps[k1]
-                       for k2 in node.deps.keys () :
+                       for k2 in list(node.deps.keys ()) :
                                if rr.match (k2) :
                                        del node.deps[k2]
 def RemoveNonLocal (deps) :
        "Remove everything not available in the current directory"
-       for k in deps.keys() :
+       for k in list(deps.keys()) :
                node = deps[k]
-               for c in node.deps.keys() :
+               for c in list(node.deps.keys()) :
                        if not os.path.exists (c) :
                                del node.deps[c]
        # also remove from the root
-       root_keys = deps.keys()
+       root_keys = list(deps.keys())
        for k in root_keys :
                if not os.path.exists (k) :
                        del deps[k]
 
 def RemoveBySymbols (deps, list) :
        "If a connection is conly caused by some symbol in 'list' it is removed"
-       for k in deps.keys() :
+       for k in list(deps.keys()) :
                node = deps[k]
-               for c in node.deps.keys () :
+               for c in list(node.deps.keys ()) :
                        edge = node.deps[c]
                        kills = []
                        for s2 in edge.symbols :
@@ -385,7 +385,7 @@ def RemoveBySymbols (deps, list) :
                                        # robustness, dont compare the empty string
                                        if s1 == "" : continue
                                        # only comparing the start of the symbol
-                                       if string.find (s2, s1) == 0 :
+                                       if s2.find (s1) == 0 :
                                                #print "removing", s2, "from", c, "->", k
                                                kills.append (s2)
                                                #NOT modifying while iterating: edge.symbols.remove (s2)
@@ -399,7 +399,7 @@ def RemoveBySymbols (deps, list) :
                                        edge.symbols.remove (s)
                # remove the symbols also from the dependency edge is pointing to
                for s in list :
-                       if s in node.exports.keys() :
+                       if s in list(node.exports.keys()) :
                                del node.exports[s]
 
 def Reduce (deps, f, bHintOnly = 1) :
@@ -407,16 +407,16 @@ def Reduce (deps, f, bHintOnly = 1) :
        # first iteration: two components are connected in both directions
        # if one connection is much weaker than the other one, the weaker one is considered bad
        # if both have similar weight they are both treated as bad and removed
-       keys = deps.keys()
+       keys = list(deps.keys())
        keys.sort()
        nReduced = 0
        for k in keys :
                node = deps[k]
-               for c in node.deps.keys () :
+               for c in list(node.deps.keys ()) :
                        edge = node.deps[c]
-                       if deps.has_key (c) :
+                       if c in deps :
                                node2 = deps[c]
-                               if node2.deps.has_key(k) :
+                               if k in node2.deps :
                                        edge2 = node2.deps[k]
                                        # if one already is reduced dont do it again
                                        if edge.reduce or edge2.reduce :
@@ -425,32 +425,32 @@ def Reduce (deps, f, bHintOnly = 1) :
                                        n2 = len(edge2.symbols)
                                        if  abs(n - n2) < 3 :
                                                f.write ("Remove (" + str(n) + "," + str(n2) +  ") " + 
edge.name + " <-> " + edge2.name +
-                                                       "\n\t" + string.join (edge.symbols, ", ") +
-                                                       "\n\t" + string.join (edge2.symbols, ", ") + "\n")
-                                               # if we now remove *both* connections some components could 
become completely disconnected. 
+                                                       "\n\t" + ", ".join (edge.symbols) +
+                                                       "\n\t" + ", ".join (edge2.symbols) + "\n")
+                                               # if we now remove *both* connections some components could 
become completely disconnected.
                                                # Instead may try some more guessing. Maybe the component 
with less deps should drop one more ?
                                                if bHintOnly :
                                                        edge.reduce = 1
                                                        edge2.reduce = 1
                                                else :
-                                                       if node.deps.has_key(c) : del node.deps[c]
-                                                       if node2.deps.has_key(k) : del node2.deps[k]
+                                                       if c in node.deps : del node.deps[c]
+                                                       if k in node2.deps : del node2.deps[k]
                                                nReduced += (n + n2)
                                        elif n > n2 :
                                                f.write ("Remove (" + str(n2) + ") " + edge.name + " -> " + 
edge2.name +
-                                                       "\n\t" + string.join (edge2.symbols, ", ") + "\n")
+                                                       "\n\t" + ", ".join (edge2.symbols) + "\n")
                                                if bHintOnly :
                                                        edge2.reduce = 1
                                                else :
-                                                       if node2.deps.has_key(k) : del node2.deps[k]
+                                                       if k in node2.deps : del node2.deps[k]
                                                nReduced += n2
-                                       else : 
-                                               f.write("Remove (" + str(n) + ") " + edge2.name + " -> " + 
edge.name + 
-                                                       "\n\t" + string.join (edge.symbols, ", ") + "\n")
+                                       else :
+                                               f.write("Remove (" + str(n) + ") " + edge2.name + " -> " + 
edge.name +
+                                                       "\n\t" + ", ".join (edge.symbols) + "\n")
                                                if bHintOnly :
                                                        edge.reduce = 1
                                                else :
-                                                       if node.deps.has_key(c) : del node.deps[c]
+                                                       if c in node.deps : del node.deps[c]
                                                nReduced += n
        f.write("Remove total: %d\n" % (nReduced))
 
@@ -459,30 +459,30 @@ def CutLeafs (deps, nCuts) :
        leafs = {}
        cut = [] # what we have cut in the last pass
        while nCuts > 0 :
-               cut = leafs.keys()
+               cut = list(leafs.keys())
                leafs = {}
                # first pass: find leafs
-               for k in deps.keys() :
+               for k in list(deps.keys()) :
                        node = deps[k]
-                       if len(node.deps.keys ()) == 0 :
+                       if len(list(node.deps.keys ())) == 0 :
                                leafs[k] = 0
-               if len(leafs.keys()) == 0 :
+               if len(list(leafs.keys())) == 0 :
                        break # nothing remaining
                nCuts -= 1
                # second pass : remove references to leafs
-               for k in deps.keys() :
+               for k in list(deps.keys()) :
                        node = deps[k]
-                       for c in leafs.keys() :
-                               if node.deps.has_key(c) :
+                       for c in list(leafs.keys()) :
+                               if c in node.deps :
                                        leafs[c] += 1
                                        del node.deps[c]
                # third pass: remove the leafs
-               for k in leafs.keys() :
-                       # we either need to reset leafs in every round or 
-                       if deps.has_key(k) :
+               for k in list(leafs.keys()) :
+                       # we either need to reset leafs in every round or
+                       if k in deps :
                                del deps[k]
-       if len(leafs.keys()) > 0 :
-               return leafs.keys()
+       if len(list(leafs.keys())) > 0 :
+               return list(leafs.keys())
        return cut
 def TransitiveReduction (deps, tops, treds=[]) :
        """If a component A has B and C as dependency and B also depends on C reduce A->C.
@@ -490,27 +490,27 @@ def TransitiveReduction (deps, tops, treds=[]) :
           'inherited' symbols and 'depth' as well.
        """
        for a in tops :
-               bs = deps[a].deps.keys()
+               bs = list(deps[a].deps.keys())
                cs = {}
                for b in bs :
-                       for c in deps[b].deps.keys() :
-                               if not c in cs.keys() :
+                       for c in list(deps[b].deps.keys()) :
+                               if not c in list(cs.keys()) :
                                        cs[c] = 1
                # bs and cs are now complete, remove all a->b, where b in cs
                extra_syms = {}
-               for c in cs.keys() :
+               for c in list(cs.keys()) :
                        if c in bs :
-                               print "Cut", a, "->", c
+                               print("Cut", a, "->", c)
                                # we need to know which edge a->b is taking the extra symbols
                                extra_syms[c] = deps[a].deps[c].symbols
                                del deps[a].deps[c]
                # fill remaining bs having a c dependency with the extra symbols
-               e_keys = extra_syms.keys()
+               e_keys = list(extra_syms.keys())
                for e in e_keys :
-                       bs = deps[a].deps.keys() # they are reduced above
+                       bs = list(deps[a].deps.keys()) # they are reduced above
                        for b in bs :
-                               if e in deps[b].deps.keys () :
-                                       print "Adjust", a, "->", b, "+", len(extra_syms[e]), "from", e
+                               if e in list(deps[b].deps.keys ()) :
+                                       print("Adjust", a, "->", b, "+", len(extra_syms[e]), "from", e)
                                        #NOT: deps[a].deps[b].symbols.extend(extra_syms[e]) # producing 
duplicates
                                        for s in extra_syms[e] :
                                                if not s in deps[a].deps[b].symbols :
@@ -518,55 +518,55 @@ def TransitiveReduction (deps, tops, treds=[]) :
                                        del extra_syms[e]
                                        break
                #FIXME: something we have cut before may not be visible anymore (would need a deeper search 
or an ordered reduction)
-               if len(extra_syms.keys()) :
-                       print "Not adjusted:", a, "->", string.join(extra_syms.keys(), ", ")
+               if len(list(extra_syms.keys())) :
+                       print("Not adjusted:", a, "->", ", ".join(list(extra_syms.keys())))
        # recurse into remaining bs
        for a in tops :
-               bs = deps[a].deps.keys()
+               bs = list(deps[a].deps.keys())
                treds.append(a) # rember already done to avoid endless recursion
                TransitiveReduction (deps, bs, treds)
 
 def TopMost (deps) :
        "everything not used by something else"
-       keys = deps.keys ()
+       keys = list(deps.keys ())
        topmost = []
        used = {}
        for k in keys :
-               for d in deps[k].deps.keys() :
-                       if not used.has_key (d) :
+               for d in list(deps[k].deps.keys()) :
+                       if d not in used :
                                used[d] = 1
        for k in keys :
-               if not used.has_key (k) :
+               if k not in used :
                        topmost.append (k)
        return topmost
 
 def RecalculateDepth (deps) :
        "From the top-most - i.e. unused - down to the bottom level "
-       keys = deps.keys ()
+       keys = list(deps.keys ())
        used = {}
        tops = []
        depth = 0
        while 1 :
                top = keys
                for k in keys :
-                       for d in deps[k].deps.keys() :
+                       for d in list(deps[k].deps.keys()) :
                                used[d] = 1
                for k in keys :
-                       if not k in used.keys () :
+                       if not k in list(used.keys ()) :
                                tops.append (k)
                if len(tops) == 0 :
                        break
                for k in tops :
                        deps[k].depth = depth
-               keys = filter (lambda x : not x in tops, keys)
+               keys = [x for x in keys if not x in tops]
                tops = []
                depth += 1
 
 def CalculateUsed (deps) :
        "Given the complete dependency tree calcualte the use count of every symbol"
-       for sn in deps.keys() :
+       for sn in list(deps.keys()) :
                node = deps[sn]
-               edge_keys = node.deps.keys()
+               edge_keys = list(node.deps.keys())
                for se in edge_keys :
                        edge = node.deps[se]
                        target = deps[se]
@@ -579,29 +579,29 @@ def UnGlob (comps) :
        for p in comps :
                g = glob.glob (p)
                for d in g :
-                       if not comp_dict.has_key (d) :
+                       if d not in comp_dict :
                                comp_dict[d] = 1
-       return comp_dict.keys()
+       return list(comp_dict.keys())
 
 def Sorted (dict) :
        "given a dictionary with name to number, sort by number"
        ret = []
-       keys = dict.keys()
+       keys = list(dict.keys())
        keys.sort ( lambda a, b : cmp(dict[a], dict[b]) )
        for k in keys :
                ret.append ((k, dict[k]))
        return ret
 # some predefined sets of DLLs, either for hiding from the dependencies or maybe to tin them
 dllsSysWin32 = [
-       "version.dll", "winmm.dll", 
+       "version.dll", "winmm.dll",
        "kernel32.dll", "user32.dll", "gdi32.dll", "comdlg32.dll", "advapi32.dll", "shell32.dll",
        "comctl32.dll", "ole32.dll", "oleaut32.dll", "winspool.drv", "imm32.dll",
-       "ws2_32.dll", "ws2help.dll", "wsock32.dll", "ntdll.dll", "mpr.dll", 
+       "ws2_32.dll", "ws2help.dll", "wsock32.dll", "ntdll.dll", "mpr.dll",
        "rpcrt4.dll", "shlwapi.dll", "netapi32.dll", "msimg32.dll", "oledlg.dll",
-       "setupapi.dll", "secur32.dll", "avifil32.dll", "msvfw32.dll", 
-       "dbghelp.dll", "uxtheme.dll", "dnsapi.dll", "activeds.dll", "crypt32.dll", 
-       "mscoree.dll", "psapi.dll", "msi.dll", "mscms.dll", "glu32.dll", "hid.dll", "imagehlp.dll", 
-       "powrprof.dll", "quartz.dll", "wininet.dll", "wintrust.dll", "riched20.dll" , "odbc32.dll", 
+       "setupapi.dll", "secur32.dll", "avifil32.dll", "msvfw32.dll",
+       "dbghelp.dll", "uxtheme.dll", "dnsapi.dll", "activeds.dll", "crypt32.dll",
+       "mscoree.dll", "psapi.dll", "msi.dll", "mscms.dll", "glu32.dll", "hid.dll", "imagehlp.dll",
+       "powrprof.dll", "quartz.dll", "wininet.dll", "wintrust.dll", "riched20.dll" , "odbc32.dll",
        "oleacc.dll", "shfolder.dll", "opengl32.dll"
        ]
 dllsCrts = [
@@ -627,7 +627,7 @@ dllsMfc = [
 dllsGtk = [
        "libglib-2.0-0.dll", "libgmodule-2.0-0.dll", "libgobject-2.0-0.dll", "libgthread-2.0-0.dll",
        "libpango-1.0-0.dll", "libpangowin32-1.0-0.dll", "libpangoft2-1.0-0.dll", "libpangocairo-1.0-0.dll",
-       "libgdk_pixbuf-2.0-0.dll", "libgdk-win32-2.0-0.dll", "libgtk-win32-2.0-0.dll", "libatk-1.0-0.dll", 
+       "libgdk_pixbuf-2.0-0.dll", "libgdk-win32-2.0-0.dll", "libgtk-win32-2.0-0.dll", "libatk-1.0-0.dll",
        "libcairo.dll", "libintl-1.dll", "iconv.dll"
        ]
 
@@ -667,21 +667,21 @@ def ParseRegexTheme (fname) :
                                sr = re.compile(m.group('regex'))
                                sc = m.group('color')
                                colorMatcher.append ((sr, sc))
-                       except re.error, msg :
-                               print "Parser error\n", s
+                       except re.error as msg :
+                               print("Parser error\n", s)
                                sys.exit(2)
        if len(colorMatcher) == 0 :
-               print "no colors parsed:", fname
+               print("no colors parsed:", fname)
                sys.exit(3)
        else :
-               print len(colorMatcher), "colors parsed:", fname
+               print(len(colorMatcher), "colors parsed:", fname)
 
 def LookupColor (node) :
        '''Used to tint the nodes by layer '''
        global colorMatcher
        if len(colorMatcher) :
                for rx, color in colorMatcher :
-                       m = rx.match(node.name) 
+                       m = rx.match(node.name)
                        if m and m.start() == 0 and m.end() == len(node.name) :
                                #print color, node.name
                                return color
@@ -689,26 +689,26 @@ def LookupColor (node) :
        # tinting by palette
        try :
                return tangoColors[node.depth]
-       except KeyError, msg :
-               print msg
+       except KeyError as msg :
+               print(msg)
                return "#ef2929" # scarlet red
-       except IndexError, msg :
-               print "Dependency", node.name, 'level', node.depth
+       except IndexError as msg :
+               print("Dependency", node.name, 'level', node.depth)
                return "#cc0000" # scarlet ret (a bit darker)
 
 def DumpSymbols (deps, f) :
        Symbols = {}
        Modules = {}
        Imports = {}
-       node_keys = deps.keys()
+       node_keys = list(deps.keys())
        node_keys.sort()
        for sn in node_keys :
                node = deps[sn]
-               if len(node.deps.keys()) == 0 :
+               if len(list(node.deps.keys())) == 0 :
                        continue
                f.write (sn + "\n")
                Imports[sn] = 0
-               edge_keys = node.deps.keys()
+               edge_keys = list(node.deps.keys())
                edge_keys.sort()
                for se in edge_keys :
                        edge = node.deps[se]
@@ -723,36 +723,36 @@ def DumpSymbols (deps, f) :
                        syms.sort()
                        for sy in syms :
                                f.write ("\t\t" + sy + "\n")
-                               if Symbols.has_key(sy) : 
-                                       Symbols[sy] += 1 
-                               else : 
+                               if sy in Symbols :
+                                       Symbols[sy] += 1
+                               else :
                                        Symbols[sy] = 1
                        Imports[sn] += len(syms)
-                       if Modules.has_key (edge.name) :
+                       if edge.name in Modules :
                                Modules[edge.name].append (node.name)
                        else :
                                Modules[edge.name] = [node.name]
        # symbols used by many modules are good candidates for refactoring
        f.write ( "***** Modules with users (symbols) *****\n" )
        for s, i in Sorted (Imports) :
-               if Modules.has_key (s) :
-                       f.write (s + " (" + str(i) + ") : " + string.join (Modules[s], ",") + "\n")
+               if s in Modules :
+                       f.write (s + " (" + str(i) + ") : " + ",".join (Modules[s]) + "\n")
                else :
                        f.write (s + " (0) : <no users>\n")
 
 def DumpUnusedSymbols (deps, f) :
-       for sd in deps.keys () :
+       for sd in list(deps.keys ()) :
                node = deps[sd]
-               for se in node.deps.keys() :
+               for se in list(node.deps.keys()) :
                        edge = node.deps[se]
                        d = deps[se]
                        for ss in edge.symbols :
-                               if ss in d.exports.keys() :
+                               if ss in list(d.exports.keys()) :
                                        del d.exports[ss]
        # after we have removed *all* used symbols
-       for sd in deps.keys () :
+       for sd in list(deps.keys ()) :
                node = deps[sd]
-               unused = node.exports.keys()
+               unused = list(node.exports.keys())
                f.write (sd + "\n")
                unused.sort ()
                for ss in unused :
@@ -761,12 +761,12 @@ def DumpUnusedSymbols (deps, f) :
 def DumpSymbolsUse (deps, f) :
        f.write ("*** Symbol Use Count ***\n")
        CalculateUsed (deps)
-       node_keys = deps.keys()
+       node_keys = list(deps.keys())
        node_keys.sort()
        for sn in node_keys :
                node = deps[sn]
                used = 0
-               for k, v in node.exports.iteritems() :
+               for k, v in node.exports.items() :
                        if v > 0 :
                                used += 1
                f.write (node.name + " (%d:%d)\n" % (used, len(node.exports)))
@@ -778,17 +778,17 @@ def DumpSymbolsUse (deps, f) :
                                f.write ("\t?\t" + sym + "\n")
 
 def DumpReverse (deps, f) :
-       node_keys = deps.keys ()
+       node_keys = list(deps.keys ())
        edges = {}
        for sn in node_keys :
                node = deps[sn]
-               edge_keys = node.deps.keys ()
+               edge_keys = list(node.deps.keys ())
                for se in edge_keys :
-                       if se in edges.keys () :
+                       if se in list(edges.keys ()) :
                                edges[se].append ((node.deps[se], sn))
                        else :
                                edges[se] = [(node.deps[se], sn)]
-       edge_keys = edges.keys ()
+       edge_keys = list(edges.keys ())
        edge_keys.sort ()
        for se in edge_keys :
                f.write (se + "\n")
@@ -798,36 +798,36 @@ def DumpReverse (deps, f) :
                        syms.sort ()
                        for sym in syms :
                                f.write ("\t" * 2 + sym + "\n")
-               
+
 def RemoveStrays (deps) :
        "Remove every node which has and is no dependency"
        strays = {}
        # does it have no dependencies?
-       for k in deps.keys() :
-               if len(deps[k].deps.keys()) == 0 :
+       for k in list(deps.keys()) :
+               if len(list(deps[k].deps.keys())) == 0 :
                        strays[k] = 1
        # even if it does not have a dependency it still may be a dependency
-       stray_keys = strays.keys()
-       for k in deps.keys() :
+       stray_keys = list(strays.keys())
+       for k in list(deps.keys()) :
                node = deps[k]
-               for d in node.deps.keys () :
+               for d in list(node.deps.keys ()) :
                        if d in stray_keys :
-                               if d in strays.keys () : # delete it only once ;)
+                               if d in list(strays.keys ()) : # delete it only once ;)
                                        #print "Not stray:", d
                                        del strays[d]
-       stray_keys = strays.keys()
+       stray_keys = list(strays.keys())
        for k in stray_keys :
-               print "Stray:", k
+               print("Stray:", k)
                del deps[k]
 
 def CreateDsm (deps) :
        "Given the complete dependency tree create a dsm sorted by 'leafs'"
        RemoveStrays(deps)
-       nTotal = len(deps.keys())
+       nTotal = len(list(deps.keys()))
        nDone = 0
        # for DSM row and colum index are the same, this is mapping the module name to it's index (row number)
        table_map = {}
-       node_keys = deps.keys()
+       node_keys = list(deps.keys())
        node_keys.sort()
        level = 0 # the iteration where we cut the leafs
        node_levels = {}
@@ -837,7 +837,7 @@ def CreateDsm (deps) :
                for sn in node_keys :
                        node = deps[sn]
                        nRefs = 0
-                       for d in node.deps.keys() : # the dependencies
+                       for d in list(node.deps.keys()) : # the dependencies
                                if d in node_keys : # still referenced
                                        nRefs = nRefs + 1
                        if nRefs == 0 :
@@ -848,15 +848,15 @@ def CreateDsm (deps) :
                        histo = {} # map number of dependencies to module
                        for sn in node_keys :
                                node = deps[sn]
-                               num = len(node.deps.keys())
-                               if not num in histo.keys() :
+                               num = len(list(node.deps.keys()))
+                               if not num in list(histo.keys()) :
                                        histo[num] = []
                                histo[num].append(sn)
                        # find modules with lowest number of dependencies
-                       for num in range(1, len(deps.keys())+1) :
-                               if num in histo.keys() :
+                       for num in range(1, len(list(deps.keys()))+1) :
+                               if num in list(histo.keys()) :
                                        leafs = histo[num]
-                                       print "Circular cut:", leafs
+                                       print("Circular cut:", leafs)
                                        break
                for sn in leafs :
                        table_map[sn] = (nTotal - nDone, level)
@@ -864,7 +864,7 @@ def CreateDsm (deps) :
                        node_levels[sn] = level
                level = level + 1
                # everything which has no reference into our tree or is allready processed
-               node_keys = filter (lambda x: not x in table_map.keys(), node_keys)
+               node_keys = [x for x in node_keys if not x in list(table_map.keys())]
        keys = Sorted (table_map)
        # now we know the index of every module in the dsm, fill cells with number of symbols
        dsm = []
@@ -875,7 +875,7 @@ def CreateDsm (deps) :
                for x, m in keys :
                        # a colum has all the dependencies of a module
                        source = deps[x]
-                       if y in source.deps.keys() :
+                       if y in list(source.deps.keys()) :
                                edge = source.deps[y]
                                # referencing the object, to later have access to all symbols, instead of 
just it's number
                                row.append (edge)
@@ -894,7 +894,7 @@ def SaveDsm (deps, f) :
        # Yippie ki-yay - Excel csv interpretation is locale dependent - Control Panel/Region/Numbers/List 
separator
        csv_list_delimiter = ','
        k, m, levels = CreateDsm (deps)
-       f.write ("Level;%s%s\n" % (csv_list_delimiter, string.join (k, csv_list_delimiter)))
+       f.write ("Level;%s%s\n" % (csv_list_delimiter, csv_list_delimiter.join (k)))
        for y in range(0, len(k)) :
                # first column: the level of dependency
                f.write ("%d" % (levels[k[y]]) + csv_list_delimiter)
@@ -917,7 +917,7 @@ def SaveDB (deps, fname) :
        # create a table containing data for reproduction
        cur.execute ('CREATE TABLE Settings (Key text, Value text)')
        cur.execute ('INSERT INTO Settings VALUES(?, ?)', ("cwd", os.getcwd()))
-       cur.execute ('INSERT INTO Settings VALUES(?, ?)', ("args", string.join (sys.argv[1:], ' ')))
+       cur.execute ('INSERT INTO Settings VALUES(?, ?)', ("args", ' '.join (sys.argv[1:])))
        con.commit()
 
        # create the dsm to have all data easily available
@@ -928,8 +928,8 @@ def SaveDB (deps, fname) :
                ' ImportedModules integer, ExportedSymbols integer, Level integer)')
        for y in range(0, len(k)) :
                # the number of imports - module count
-               num_imp = len(deps[k[y]].deps.keys())
-               num_exp = len(deps[k[y]].exports.keys())
+               num_imp = len(list(deps[k[y]].deps.keys()))
+               num_exp = len(list(deps[k[y]].exports.keys()))
                cur.execute ('INSERT INTO Modules VALUES(?, ?, ?, ?)', (k[y], num_imp, num_exp, levels[k[y]]))
        con.commit()
        # create an index of ModuleNames
@@ -958,7 +958,7 @@ def SaveDB (deps, fname) :
        # add unused symbols, too
        for sm in k :
                node = deps[sm]
-               for e, used in node.exports.iteritems() :
+               for e, used in node.exports.items() :
                        if used == 0 : # the used ones are already added above
                                cur.execute ('INSERT INTO Symbols VALUES(?,?,?)', (e, None, sm))
        con.commit ()
@@ -1004,11 +1004,11 @@ def CreateList (s) :
                        if not line[0] in ["#", ";"] :
                                lst.append (line[:-1].lower())
        else :
-               lst = string.split (s, ",")
+               lst = s.split (",")
        return lst
 
 def ImportDump (sfDump, deps) :
-       print "Import from:", sfDump
+       print("Import from:", sfDump)
        global g_DontFollow
        deps = {}
        f = open (sfDump)
@@ -1030,7 +1030,7 @@ def ImportDump (sfDump, deps) :
                                curEdge = None
                                symbols = []
                        k = m.group(0)
-                       if k in deps.keys() :
+                       if k in list(deps.keys()) :
                                curNode = deps[k]
                        else :
                                curNode = deps[k] = Node (k, 0) # Todo: depth?
@@ -1038,7 +1038,7 @@ def ImportDump (sfDump, deps) :
                        m = rEdge.match(s)
                        if m :
                                if not curNode.name == m.group(1) :
-                                       print "???", s
+                                       print("???", s)
                                        sys.exit(2)
                                else :
                                        if curNode and curEdge and len(symbols) :
@@ -1048,7 +1048,7 @@ def ImportDump (sfDump, deps) :
                                                symbols = []
                                        curEdge = m.group(2)
                                        # have to create the node, too
-                                       if not curEdge in deps.keys() :
+                                       if not curEdge in list(deps.keys()) :
                                                deps[curEdge] = Node(curEdge, curNode.depth+1)
                        else :
                                m = rSym.match (s)
@@ -1088,42 +1088,42 @@ def main () :
 
        if IsWin32() :
                # check if we are running from the right environment
-               # HB: completely bogus, only works with vc2003? 
+               # HB: completely bogus, only works with vc2003?
                #try :
                #       print "FrameworkSDKDir=" + os.environ["FrameworkSDKDir"]
                #except :
                #       print "The script needs to be called from a 'VS command prompt'"
                #       sys.exit (1)
                if FindInPath ("dumpbin.exe") == "dumpbin.exe" :
-                       print "dumpbin.exe not found"
+                       print("dumpbin.exe not found")
                        sys.exit (1)
        for arg in sys.argv[1:] :
-               if string.find (arg, "--remove") == 0 :
+               if arg.find ("--remove") == 0 :
                        if arg == "--remove-sys" : dllsToRemove.extend (dllsSysWin32)
                        elif arg == "--remove-crt" : dllsToRemove.extend (dllsCrts)
                        elif arg == "--remove-mfc" : dllsToRemove.extend (dllsMfc)
                        elif arg == "--remove-gtk" : dllsToRemove.extend (dllsGtk)
                        elif arg == "--remove-non-local" : bRemoveNonLocal = 1
-                       elif string.find (arg, "--remove-regex=") == 0 :
+                       elif arg.find ("--remove-regex=") == 0 :
                                regexRemoves.append (arg[len("--remove-regex="):])
-                       elif string.find (arg, "--remove-symbols=") == 0 :
+                       elif arg.find ("--remove-symbols=") == 0 :
                                noSyms = CreateList(arg[len("--remove-symbols="):])
                                symbolsToRemove.extend (noSyms)
                        else :
                                noDeps = CreateList(arg[len("--remove="):])
                                dllsToRemove.extend(noDeps)
-               elif string.find (arg, "--dont-follow") == 0 :
+               elif arg.find ("--dont-follow") == 0 :
                        global g_DontFollow
                        noFollow = CreateList(arg[len("--dont-follow="):])
                        g_DontFollow.extend (noFollow)
-               elif string.find (arg, "--depth=") == 0 :
+               elif arg.find ("--depth=") == 0 :
                        nMaxDepth = int(arg[len("--depth="):])
-                       if nMaxDepth < 1 : print  "Wrong depth"; sys.exit(1)
-               elif string.find (arg, "--symbols=") == 0 :
+                       if nMaxDepth < 1 : print("Wrong depth"); sys.exit(1)
+               elif arg.find ("--symbols=") == 0 :
                        nSymbols = int(arg[len("--symbols="):])
                        if nSymbols < 0 : nSymbols = 0
-               elif string.find (arg, "--cut-leafs") == 0 :
-                       if string.find (arg, "--cut-leafs=") == 0 :
+               elif arg.find ("--cut-leafs") == 0 :
+                       if arg.find ("--cut-leafs=") == 0 :
                                nCutLeafs = int(arg[len("--cut-leafs="):])
                        else :
                                nCutLeafs = 10000 # infinite ;)
@@ -1151,77 +1151,77 @@ def main () :
                        bTred = 1
                elif arg == "--by-use" :
                        bByUse = 1
-               elif string.find (arg, "--pickle=") == 0 :
+               elif arg.find ("--pickle=") == 0 :
                        sGraph = arg[len("--pickle="):]
                        sPickle = arg[len("--pickle="):] + ".pickle"
-               elif string.find (arg, "--path") == 0 :
+               elif arg.find ("--path") == 0 :
                        global g_path
-                       if string.find (arg, "--path=") == 0 :
+                       if arg.find ("--path=") == 0 :
                                g_path = arg[len("--path="):]
                        else :
                                g_path = os.environ['PATH']
-               elif string.find (arg, "--theme=") == 0 :
+               elif arg.find ("--theme=") == 0 :
                        theme = arg[len("--theme="):]
                        if theme == 'regex' :
                                ParseRegexTheme('wdeps.tinting')
                        else :
-                               print "Unknown theme!"
+                               print("Unknown theme!")
                                sys.exit(1)
-               elif string.find (arg, "--") == 0 :
-                       print "Unknown option or missing parameter:", arg
+               elif arg.find ("--") == 0 :
+                       print("Unknown option or missing parameter:", arg)
                        sys.exit(1)
                else :
                        if len(components) == 0 :
-                               components = string.split(arg, ",")
+                               components = arg.split(",")
                                components = UnGlob (components)
                                # don't GetDeps here cause we need to first evaluate *all* parameters
                                if len(components) == 0 :
-                                       print "No DLL input found - wrong directory?"
+                                       print("No DLL input found - wrong directory?")
                                        break
                                sGraph = components[0]
                        elif len(arg)>0 :
                                sOutFilename = arg
-               print "arg is:", arg 
+               print("arg is:", arg)
        if len(sys.argv) < 2 :
-               print sys.argv[0], "[parameters] <component,s> [dot-output]"
-               print """
+               print(sys.argv[0], "[parameters] <component,s> [dot-output]")
+               print("""
 Given one or mores starting components like
 
        a.dll,another.dll
 
 this tool anayzes the dependencies of the DLLs and generates a depency graph
 in the dot format (see: www.graphviz.org). This tool requires DUMPBIN from
-the MSVC toolchain. With the version included in VC7.1 it is capable to also 
-'see' 'delay load imports'. The tool follows the depencies to DLLs in the 
+the MSVC toolchain. With the version included in VC7.1 it is capable to also
+'see' 'delay load imports'. The tool follows the depencies to DLLs in the
 current directory only.
 
-If a second parameter is given the depency graph is written to that file. 
+If a second parameter is given the depency graph is written to that file.
 Otherwise output is written to stdout.
 
 There is also a number of extra parameters to fine-tune the graph.
 First you can use --remove{-crt,-sys,-mfc} to remove the respective
-set of dependencies. There is also a special form of remove 
+set of dependencies. There is also a special form of remove
 --remove=another.dll removes excatly that dll from the graph.
 
-Another way to get more manageable graphs is to limit the recursion 
-depth while searching, like --depth=2 which cuts everything below 
+Another way to get more manageable graphs is to limit the recursion
+depth while searching, like --depth=2 which cuts everything below
 the second dependency level.
 
 The most useful switch to anaylze parts of a highly coupled system is
 --dont-follow=random.dll (accepting a list as above). This will show
-random.dll in the resulting graph in gray. But it will not drag in 
+random.dll in the resulting graph in gray. But it will not drag in
 any dependencies below.
 
-If you have a small graph it may be useful to not only show the 
-number of symbols used but their name. Giving --symbols=n all edges 
-with n or less symbols will have the (mostly demangled) name of the 
+If you have a small graph it may be useful to not only show the
+number of symbols used but their name. Giving --symbols=n all edges
+with n or less symbols will have the (mostly demangled) name of the
 symbols printed as text.
 
 Finally there is the switch --dump for people prefering the textual
 representation over some graph.
 
 For more information read the source.
-"""
+""")
                sys.exit(0)
 
        # output ...
@@ -1232,12 +1232,12 @@ For more information read the source.
                f = open(sOutFilename, "w")
 
        if bDump : # remember the command line
-               f.write ("# " + string.join (sys.argv, " ") + "\n")
+               f.write ("# " + " ".join (sys.argv) + "\n")
 
        try :
                import pickle
                deps = pickle.load (open(sPickle))
-               print "Input from", sPickle
+               print("Input from", sPickle)
        except :
                for s in components:
                        if IsWin32() :
@@ -1245,7 +1245,7 @@ For more information read the source.
                        else :
                                GetDepsPosix (s, deps, nMaxDepth)
                # with a bit dirty condition, try to populate from .dump
-               if len(deps.keys()) == 1 and len(components) == 1:
+               if len(list(deps.keys())) == 1 and len(components) == 1:
                        deps = ImportDump (components[0], deps)
                        RecalculateDepth (deps)
 
@@ -1263,13 +1263,13 @@ For more information read the source.
        while nCutLeafs > 0 :
                # not always iterating here, CutLeafs does too
                if bDump :
-                       nTotal = len(deps.keys())
+                       nTotal = len(list(deps.keys()))
                        leafs = CutLeafs (deps, 1)
                        if len(leafs) < 1 :
                                break
                        f.write ("CutLeafs " + str(nTotal) + " => " + str(nTotal - len(leafs)) + "\n")
                        leafs.sort()
-                       f.write ("\t" + string.join (leafs, ",") + "\n")
+                       f.write ("\t" + ",".join (leafs) + "\n")
                        nCutLeafs -= 1
                else :
                        leafs = CutLeafs (deps, nCutLeafs)
@@ -1277,7 +1277,7 @@ For more information read the source.
 
        if bDump :
                topmost = TopMost(deps)
-               f.write ("TopMost(%d) : %s\n" % (len(topmost), string.join(topmost, ",")))
+               f.write ("TopMost(%d) : %s\n" % (len(topmost), ",".join(topmost)))
 
        # the *automatic* reduction is intentionally after cut-leafs
        if bReduce :
@@ -1287,17 +1287,17 @@ For more information read the source.
                        f2 = open (sOutFilename + ".reduced.txt", "w")
                Reduce (deps, f2)
                if nCutLeafs > 1000 : # magic number two, still infinite ;)
-                       for d in deps.keys() :
+                       for d in list(deps.keys()) :
                                # kind of arbitrary numbers
-                               n1 = len(deps[d].deps.keys()) # lthis ones dependencies
-                               n2 = len(deps.keys()) # total number of components left
+                               n1 = len(list(deps[d].deps.keys())) # lthis ones dependencies
+                               n2 = len(list(deps.keys())) # total number of components left
                                # write erros/warning like the compiler would do
                                f2.write ("%s - %d error(s), %d warning(s)\n" % (deps[d].name, n1, n2))
        if bTred :
                tops = TopMost (deps)
                if len(tops) == 0 :
-                       print "Transitive reduction without topmost!"
-                       tops = deps.keys ()
+                       print("Transitive reduction without topmost!")
+                       tops = list(deps.keys ())
                TransitiveReduction (deps, tops)
 
        if sPickle :
@@ -1329,14 +1329,14 @@ For more information read the source.
 
 def SaveXml (deps, f) :
        "simple XML dump"
-       f.write('<nodes count="%d">\n' % (len(deps.keys())))
-       f.write('\t'*1 + '<command>wdeps.py ' + string.join (sys.argv[1:], ' ') + '</command>\n')
-       deps_keys = deps.keys()
+       f.write('<nodes count="%d">\n' % (len(list(deps.keys()))))
+       f.write('\t'*1 + '<command>wdeps.py ' + ' '.join (sys.argv[1:]) + '</command>\n')
+       deps_keys = list(deps.keys())
        deps_keys.sort()
        for sn in deps_keys :
                node = deps[sn]
                f.write('\t'*1 + '<node name="%s" symbols="%d">\n' % (sn, len(node.exports)))
-               edge_keys = node.deps.keys()
+               edge_keys = list(node.deps.keys())
                if edge_keys :
                        edge_keys.sort()
                        f.write('\t'*2 + '<edges count="%d">\n' % (len(edge_keys)))
@@ -1356,73 +1356,73 @@ def SaveXml (deps, f) :
 
 def SaveDt (deps, f) :
        """ see: http://dtangler.org """
-       deps_keys = deps.keys()
+       deps_keys = list(deps.keys())
        deps_keys.sort()
        for sn in deps_keys :
                node = deps[sn]
-               edge_keys = node.deps.keys()
+               edge_keys = list(node.deps.keys())
                if not edge_keys :
                        continue
                edge_keys.sort()
-               f.write (sn + " : " + string.join (edge_keys, " ") + "\n")
+               f.write (sn + " : " + " ".join (edge_keys) + "\n")
 
 def SaveDot (deps, sGraph, bByUse, nSymbols, f) :
        # build the graph
        f.write ('digraph "' + sGraph + '" {\n')
-       f.write ('graph [fontsize=8.0 label="wdeps.py ' + string.join (sys.argv[1:], " ") 
-                       + '\\n' + time.ctime() + '"]\n') 
+       f.write ('graph [fontsize=8.0 label="wdeps.py ' + " ".join (sys.argv[1:])
+                       + '\\n' + time.ctime() + '"]\n')
        f.write ('ratio=0.7\nnode [fontsize=12.0 ]\nedge [fontsize=8.0]\n')
        if bByUse :
                # kind of inverted diagram not showing edependencies but 'users'
                users = {}
-               for sn in deps.keys() :
+               for sn in list(deps.keys()) :
                        users[sn] = []
-               for sn in deps.keys() :
+               for sn in list(deps.keys()) :
                        node = deps[sn]
-                       for se in node.deps.keys() :
+                       for se in list(node.deps.keys()) :
                                edge = node.deps[se]
                                users[edge.name].append(node.name)
-               for sn in users.keys() :
+               for sn in list(users.keys()) :
                        for s in users[sn] :
                                f.write ('"%s" -> "%s"\n' % (sn, s))
        else :
                # first pass mark don't follows
                dontFollowsDone = {}
                urlsDone = {}
-               deps_keys = deps.keys()
+               deps_keys = list(deps.keys())
                deps_keys.sort()
                for sn in deps_keys :
                        node = deps[sn]
-                       edge_keys = node.deps.keys()
+                       edge_keys = list(node.deps.keys())
                        edge_keys.sort()
                        for se in edge_keys :
                                cut_len = len(se)
                                if IsWin32() :
-                                       cut_len = string.find(se, ".dll")
+                                       cut_len = se.find(".dll")
                                else :
-                                       cut_len = string.find(se, ".so") 
+                                       cut_len = se.find(".so")
                                ses = se[:cut_len]
                                if se in g_DontFollow :
-                                       if not dontFollowsDone.has_key(se) :
+                                       if se not in dontFollowsDone :
                                                # mark as such
                                                f.write ('"%s" [style=filled,fillcolor="lightgray"]\n' % 
(se,))
                                                dontFollowsDone[se] = 1
                                else :
-                                       if not urlsDone.has_key (se) :
+                                       if se not in urlsDone :
                                                f.write ('"%s" [style=filled,fillcolor="%s",URL="#%s"]\n' % 
(se, LookupColor(deps[se]), se))
                                                urlsDone[se] = 1
-                                               
-                                       
+
+
                for sn in deps_keys :
                        # write weighted edges, could also classify the nodes ...
                        node = deps[sn]
-                       edge_keys = node.deps.keys()
+                       edge_keys = list(node.deps.keys())
                        edge_keys.sort()
                        for se in edge_keys :
                                edge = node.deps[se]
                                sPrefix = ""
                                if edge.reduce : # remove from graph by commenting it out
-                                       sPrefix = "// " + string.join(edge.symbols, ", ") + "\n// "
+                                       sPrefix = "// " + ", ".join(edge.symbols) + "\n// "
                                if edge.delayLoad :
                                        # putting 'weight' and 'constraint' seems to be too much for dot
                                        sStyle = "weight=%f,style=dotted" % 
(math.log10(math.sqrt(edge.Weight()+.1)),)
@@ -1432,11 +1432,11 @@ def SaveDot (deps, sGraph, bByUse, nSymbols, f) :
                                        sStyle = "weight=%f" % (math.log10(edge.Weight()+.1),)
                                if edge.Weight() <= nSymbols :
                                        #f.write ('"%s" -> "%s" [weight=%f,label=%s]\n' % (node.name, 
edge.name, math.log(1)-0.5, edge.symbols[0]))
-                                       f.write ('%s"%s" -> "%s" [fontsize=6,label="%s",%s]\n' 
-                                                       % (sPrefix, node.name, edge.name, 
string.join(edge.symbols, "\\n"), sStyle))
+                                       f.write ('%s"%s" -> "%s" [fontsize=6,label="%s",%s]\n'
+                                                       % (sPrefix, node.name, edge.name, 
"\\n".join(edge.symbols), sStyle))
                                else :
                                        #f.write ('"%s" -> "%s" [weight=%f]\n' % (node.name, edge.name, 
math.log(edge.Weight())-0.5))
-                                       f.write ('%s"%s" -> "%s" [label="(%d)",%s]\n' 
+                                       f.write ('%s"%s" -> "%s" [label="(%d)",%s]\n'
                                                        % (sPrefix, node.name, edge.name, edge.Weight(), 
sStyle))
        f.write("}\n")
 
diff --git a/samples/gobj-parse.py b/samples/gobj-parse.py
index 03105a4de..030cc3339 100644
--- a/samples/gobj-parse.py
+++ b/samples/gobj-parse.py
@@ -1,11 +1,11 @@
 '''
-Parses class definitions out of GObject based headers 
+Parses class definitions out of GObject based headers
 
-ToDo: 
+ToDo:
        - better parser aproach ;)
        - respect /*< public,protected,private >*/
        - parse *.c
-       
+
 '''
 #    Copyright (c) 2006, Hans Breuer <hans breuer org>
 
@@ -23,40 +23,40 @@ ToDo:
 #   along with this program; if not, write to the Free Software
 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-import glob, os, string, re
+import glob, os, re
 
 verbose = 0
 
 def strip_whitespace (s) :
-       s = string.replace (s, " ", "")
-       return string.replace (s, "\t", "")
+       s = s.replace (" ", "")
+       return s.replace ("\t", "")
 
 class CMethod :
        def __init__ (self, name, type) :
-               self.name = string.strip(name)
+               self.name = name.strip()
                self.pars = []
-               self.retval = string.replace(type, ' ', '')
+               self.retval = type.replace(' ', '')
        def AddPar (self, name, type) :
-               self.pars.append ((string.strip(name), 
+               self.pars.append ((name.strip(),
                                         strip_whitespace(type)))
 
 class CClass :
        def __init__ (self, name) :
-               self.name = string.strip(name)
+               self.name = name.strip()
                self.defs = {'TYPE' : None, 'OBJECT' : None, 'CLASS' : None}
                self.parent = None
                self.attrs = []
                self.methods = {}
                self.signals = []
        def AddAttr (self, name, type) :
-               if verbose : print "\t", type, name
-               self.attrs.append ((string.strip(name), 
+               if verbose : print("\t", type, name)
+               self.attrs.append ((name.strip(),
                                          strip_whitespace(type)))
        def AddMethod (self, name, method) :
-               if verbose : print "\t", name, "()"
-               self.methods[string.strip(name)] = method
+               if verbose : print("\t", name, "()")
+               self.methods[name.strip()] = method
        def AddSignal (self, name, type) :
-               self.signals.append ((string.strip(name), 
+               self.signals.append ((name.strip(),
                                            strip_whitespace(type)))
 
 class CStripCommentsAndBlankLines :
@@ -70,12 +70,12 @@ class CStripCommentsAndBlankLines :
                incom = 0
                r = re.compile("^\s*$") # to remove empty lines
                for s in lines :
-                       x1 = string.find (s, '/*')
-                       x2 = string.find (s, '*/')
+                       x1 = '/*'.find (s)
+                       x2 = '*/'.find (s)
                        while x2 > x1 and incom != 1 :
                                s = s[:x1] + s[x2+2:]
-                               x1 = string.find (s, '/*')
-                               x2 = string.find (s, '*/')
+                               x1 = '/*'.find (s)
+                               x2 = '*/'.find (s)
                        else :
                                if x1 > -1 :
                                        incom = 1
@@ -102,7 +102,7 @@ def TestStripFile (name) :
        f = CStripCommentsAndBlankLines(name)
        s = f.readline()
        while s :
-               print s[:-1]
+               print(s[:-1])
                s = f.readline()
 
 import sys
@@ -110,7 +110,7 @@ import sys
 sPkg = ''
 sDir = '.'
 if len (sys.argv) < 2 :
-       print sys.argv[0], '<package>', '[directory]'
+       print(sys.argv[0], '<package>', '[directory]')
        sys.exit(0)
 else :
        sPkg = sys.argv[1]
@@ -128,7 +128,7 @@ no_klass_headers = []
 #
 # #define GDK_DRAWABLE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DRAWABLE, GdkDrawable))
 #
-rClassCast = re.compile ("^#define " + string.upper (sPkg) + "_(?P<n1>\w+)" \
+rClassCast = re.compile ("^#define " + sPkg.upper () + "_(?P<n1>\w+)" \
                                 "\((?P<par>\w+)\)\s+\(" \
                                 "((G_TYPE_CHECK_INSTANCE_CAST)|(GTK_CHECK_CAST))\s+" \
                                 "\(\((?P=par)\),\s*(?P<type>\w+),\s+" \
@@ -165,18 +165,18 @@ for sF in lst :
                        sName = m.group('name')
                        unresolved[sName] = CClass (sName)
                s = fin.readline ()
-       if len(unresolved.keys()) == 0 :
+       if len(list(unresolved.keys())) == 0 :
                no_klass_headers.append (sF)
        else :
                iFound = 0
-               unresolved_keys = unresolved.keys()
+               unresolved_keys = list(unresolved.keys())
                for sK in unresolved_keys :
                        klass = unresolved[sK]
                        sK = klass.name
                        # start from the beginning
                        fin.seek (0)
                        rObject = re.compile ("struct\s+_" + sK + "\s*(?P<p>\{*)\s*$")
-                       rKlass  = re.compile ("struct\s+_" + sK + "Class\s*(?P<p>\{*)\s*$") 
+                       rKlass  = re.compile ("struct\s+_" + sK + "Class\s*(?P<p>\{*)\s*$")
                        s = fin.readline ()
                        meth = None
                        while s :
@@ -187,14 +187,13 @@ for sF in lst :
                                        s = fin.readline() # read parent line
                                        mV = rVarDecl.match(s)
                                        try :
-                                               klass.parent = (mV.group('name'), 
-                                                                   string.strip(mV.group('type')))
-                                               if verbose : print "class", sK, ":", mV.group('type')
+                                               klass.parent = (mV.group('name'), mV.group('type').strip())
+                                               if verbose : print("class", sK, ":", mV.group('type'))
                                        except :
-                                               print 'klass.parent error (', sF, ') :', s
+                                               print('klass.parent error (', sF, ') :', s)
                                        s = fin.readline ()
                                        mV = rVarDecl.match(s)
-                                       if not mV and verbose : print s 
+                                       if not mV and verbose : print(s)
                                        while mV :
                                                klass.AddAttr (mV.group('name'), mV.group('type'))
                                                s = fin.readline ()
@@ -219,13 +218,13 @@ for sF in lst :
                                                        elif mM :
                                                                meth = CMethod (mM.group('name'), 
mM.group('type'))
                                                                klass.AddMethod (mM.group('name'), meth)
-                                                               meth.AddPar (mM.group('pname'), 
mM.group('ptype')) 
+                                                               meth.AddPar (mM.group('pname'), 
mM.group('ptype'))
                                                                if mM.group('done') == ');' :
-                                                                       meth = None # reset 
+                                                                       meth = None # reset
                                                        elif meth :
                                                                mP = rPar.match (s)
                                                                if mP :
-                                                                       meth.AddPar (mP.group('pname'), 
mP.group('ptype')) 
+                                                                       meth.AddPar (mP.group('pname'), 
mP.group('ptype'))
                                                                        if mP.group('done') == ');' :
                                                                                meth = None # reset
                                                                else :
@@ -241,11 +240,11 @@ for sF in lst :
                if iFound == 0 :
                        no_klass_headers.append (sF)
                else :
-                       print sF + " (" + str(iFound) + ")"
+                       print(sF + " (" + str(iFound) + ")")
 
 for sF in no_klass_headers :
        fout.write ("<No Klass>: " + sF + "\n")
-print 'Klasses found:', len(klasses), '\nHeaders w/o classes:', len(no_klass_headers)
+print('Klasses found:', len(klasses), '\nHeaders w/o classes:', len(no_klass_headers))
 
 def CmpParent (a,b) :
        'gets CClass, sort by parent type'
@@ -265,9 +264,9 @@ def CmpParent (a,b) :
                elif cmp(b.name, a.parent[1]) == 0 :
                        #print b.name, ">", a.name
                        return -1
-               return i 
+               return i
        except :
-               print "Sort Error:", str(a.parent), str(b.parent)
+               print("Sort Error:", str(a.parent), str(b.parent))
                return 0
 
 klasses.sort(CmpParent)
@@ -280,9 +279,9 @@ for k in klasses :
        if k.parent :
                parents[k.parent[1]] = 1
 for k in klasses :
-       if parents.has_key(k.name) :
+       if k.name in parents :
                del parents[k.name]
-sorted.extend(parents.keys())
+sorted.extend(list(parents.keys()))
 # sort the rest
 while len(sorted_klasses) < len(klasses) :
        before = len(sorted_klasses)
@@ -305,7 +304,7 @@ while len(sorted_klasses) < len(klasses) :
                        for k in klasses :
                                if not k.name in sorted :
                                        unsorted.append(k.name)
-                       print string.join(unsorted, ", "), "not sorted?"
+                       print(", ".join(unsorted), "not sorted?")
                break # avoid endless loop
 
 klasses = sorted_klasses
@@ -326,7 +325,7 @@ def WritePython (fname) :
                        fpy.write ('\t\t # Signals\n')
                        for attr in klass.signals :
                                fpy.write ('\t\tself.' + attr[0] + " = None # " + attr[1] + "\n")
-               for s in klass.methods.keys() :
+               for s in list(klass.methods.keys()) :
                        meth = klass.methods[s]
                        fpy.write ('\t#returns: ' + meth.retval + '\n\tdef ' + meth.name + ' (')
                        s1 = ''
@@ -440,23 +439,23 @@ def WriteDia (fname) :
        for klass in klasses :
                if klass.parent : # add every parent ...
                        parentName = klass.parent[1]
-                       if externals.has_key (parentName) :
+                       if parentName in externals :
                                externals[parentName] += 1
                        else :
                                externals[parentName] = 1
        for klass in klasses :
-               if externals.has_key (klass.name) : # ... but remove  the internals
+               if klass.name in externals : # ... but remove  the internals
                        del externals[klass.name]
-       
+
        # write all 'external' parents
-       for s in externals.keys() :
+       for s in list(externals.keys()) :
                externals[s] = (nObject, -1)
                fdia.write(sStartClass % (nObject, x, y, s))
                positions[s] = (x,y)
                x += dx
                fdia.write(sFillColorAttribute % ("#ffff00",))
                # fixme: any more attributes?
-               fdia.write (sEndObject)         
+               fdia.write (sEndObject)
                nObject += 1
 
        for klass in klasses :
@@ -464,7 +463,7 @@ def WriteDia (fname) :
                if klass.parent :
                        parentName = klass.parent[1]
                        connectFrom[klass.name] = (nObject, parentName)
-               if positions.has_key (parentName) :
+               if parentName in positions :
                        x = positions[parentName][0] # same x
                        y = positions[parentName][1] + dy # y below
                else :
@@ -474,48 +473,48 @@ def WriteDia (fname) :
                fdia.write(sStartClass % (nObject, x, y, klass.name))
                positions[klass.name] = (x, y)
                if len (klass.attrs) > 0 :
-                       fdia.write (sStartAttributes)                   
+                       fdia.write (sStartAttributes)
                        for attr in klass.attrs :
-                               fdia.write (sDataAttribute % (attr[0], attr[1]))                              
  
+                               fdia.write (sDataAttribute % (attr[0], attr[1]))
                        fdia.write (sEndAttributes)
 #              if len (klass.signals) > 0 :
 #                      fpy.write ('\t\t # Signals\n')
 #                      for attr in klass.signals :
 #                              fpy.write ('\t\tself.' + attr[0] + " = None # " + attr[1] + "\n")
                # the differnence between signals and methods is in the attributes
-               if len (klass.signals) > 0 or len(klass.methods.keys()) > 0  :
+               if len (klass.signals) > 0 or len(list(klass.methods.keys())) > 0  :
                        fdia.write (sStartOperations)
-               for s in klass.methods.keys() :
+               for s in list(klass.methods.keys()) :
                        meth = klass.methods[s]
                        fdia.write(sStartOperation % (meth.name, meth.retval))
                        # first parameter is supposed to be 'this' pointer: leave out
                        for par in meth.pars[1:] :
                                fdia.write (sDataParameter % (par[0], par[1]))
                        fdia.write (sEndOperation)
-               if len (klass.signals) > 0 or len(klass.methods.keys()) > 0  :
+               if len (klass.signals) > 0 or len(list(klass.methods.keys())) > 0  :
                        fdia.write (sEndOperations)
                fdia.write (sEndObject)
                nObject += 1
 
        # write all connections
-       for sFrom in connectFrom.keys() :
+       for sFrom in list(connectFrom.keys()) :
                iFrom = connectFrom[sFrom][0]
                sTo = connectFrom[sFrom][1]
-               if connectFrom.has_key (sTo) :
+               if sTo in connectFrom :
                        iTo = connectFrom[sTo][0]
-               elif externals.has_key(sTo) :
+               elif sTo in externals :
                        iTo = externals[sTo][0]
                else :
-                       print "sFrom -> sTo?", sFrom, sTo
+                       print("sFrom -> sTo?", sFrom, sTo)
                        continue # something wrong?
                nObject += 1
                #fdia.write ('\n\t<!-- %s : %s -->' % (sFrom, sTo))
                # just to give it some position (and stop Dia complaining)
-               if positions.has_key (sTo) :
+               if sTo in positions :
                        x1, y1 = positions[sTo]
                else :
                        x1, y1 = (dx, dy)
-               if positions.has_key (sFrom) :
+               if sFrom in positions :
                        x2, y2 = positions[sFrom]
                else :
                        x2, y2 = (dx, dy)
@@ -523,14 +522,14 @@ def WriteDia (fname) :
                fdia.write (sOrthPoints % (x1,y1, x1,(y1+y2)/2, x2,(y1+y2)/2, x2,y2 ))
                # and connect
                fdia.write ('''
-      <dia:connections>        
+      <dia:connections>
         <dia:connection handle="0" to="O%d" connection="6"/>
         <dia:connection handle="1" to="O%d" connection="1"/>
       </dia:connections>''' % (iTo, iFrom) )
                fdia.write (sEndObject)
 
        fdia.write (sEndDiagram)
-       print len(connectFrom.keys()), " connections"
+       print(len(list(connectFrom.keys())), " connections")
 
 WritePython (sPkg + "-generated.py")
 WriteDia (sPkg + "-generated.dia")


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