[pygobject] Add Python implementation of GError



commit 3083daf420ac1900bb20604c22fd61e5187b4ae8
Author: Simon Feltman <sfeltman src gnome org>
Date:   Sun May 4 04:13:46 2014 -0700

    Add Python implementation of GError
    
    Add internally used gi/_error.py module as a basis for implementing
    a unified GError between introspection and static bindings. Patch Python
    implementations of GError.matches and GError.new_literal in the GLib
    overrides
    
    https://bugzilla.gnome.org/show_bug.cgi?id=712519

 Makefile.am          |    3 +-
 gi/_error.py         |   53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gi/_option.py        |    3 +-
 gi/overrides/GLib.py |   25 ++++++++++++++++++++++-
 gi/pygi-error.c      |   18 ++++++----------
 tests/test_error.py  |   37 ++++++++++++++++++++++++++++++++++
 6 files changed, 125 insertions(+), 14 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 208ed13..968b6c9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,7 +65,8 @@ nobase_pyexec_PYTHON = \
        gi/_constants.py \
        gi/_propertyhelper.py \
        gi/_signalhelper.py \
-       gi/_option.py
+       gi/_option.py \
+       gi/_error.py
 
 # if we build in a separate tree, we need to symlink the *.py files from the
 # source tree; Python does not accept the extensions and modules in different
diff --git a/gi/_error.py b/gi/_error.py
new file mode 100644
index 0000000..34a5317
--- /dev/null
+++ b/gi/_error.py
@@ -0,0 +1,53 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# Copyright (C) 2014 Simon Feltman <sfeltman gnome org>
+#
+#   _error.py: GError Python implementation
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+
+# NOTE: This file should not have any dependencies on introspection libs
+# like gi.repository.GLib because it would cause a circular dependency.
+# Developers wanting to use the GError class in their applications should
+# use gi.repository.GLib.GError
+
+
+class GError(RuntimeError):
+    def __init__(self, message='unknown error', domain='pygi-error', code=0):
+        super(GError, self).__init__(message)
+        self.message = message
+        self.domain = domain
+        self.code = code
+
+    def __str__(self):
+        return "%s: %s (%d)" % (self.domain, self.message, self.code)
+
+    def __repr__(self):
+        return "GLib.GError('%s', '%s', %d)" % (self.message, self.domain, self.code)
+
+    def copy(self):
+        return GError(self.message, self.domain, self.code)
+
+    def matches(self, domain, code):
+        """Placeholder that will be monkey patched in GLib overrides."""
+        raise NotImplementedError
+
+    @staticmethod
+    def new_literal(domain, message, code):
+        """Placeholder that will be monkey patched in GLib overrides."""
+        raise NotImplementedError
diff --git a/gi/_option.py b/gi/_option.py
index 4ffeb1d..2d30abf 100644
--- a/gi/_option.py
+++ b/gi/_option.py
@@ -40,7 +40,8 @@ else:
     _basestring = basestring
     _bytes = str
 
-from gi._gi import _glib, GError
+from gi._gi import _glib
+from gi._error import GError
 GLib = get_introspection_module('GLib')
 
 OPTION_CONTEXT_ERROR_QUARK = GLib.quark_to_string(GLib.option_error_quark())
diff --git a/gi/overrides/GLib.py b/gi/overrides/GLib.py
index c2462e3..d25d374 100644
--- a/gi/overrides/GLib.py
+++ b/gi/overrides/GLib.py
@@ -39,7 +39,9 @@ __all__.append('option')
 
 
 # Types and functions still needed from static bindings
-from gi._gi import _glib, GError
+from gi._gi import _glib
+from gi._error import GError
+
 OptionContext = _glib.OptionContext
 OptionGroup = _glib.OptionGroup
 Pid = _glib.Pid
@@ -51,6 +53,27 @@ def threads_init():
                   'See: https://wiki.gnome.org/PyGObject/Threading',
                   PyGIDeprecationWarning, stacklevel=2)
 
+
+def gerror_matches(self, domain, code):
+    # Handle cases where self.domain was set to an integer for compatibility
+    # with the introspected GLib.Error.
+    if isinstance(self.domain, str):
+        self_domain_quark = GLib.quark_from_string(self.domain)
+    else:
+        self_domain_quark = self.domain
+    return (self_domain_quark, self.code) == (domain, code)
+
+
+def gerror_new_literal(domain, message, code):
+    domain_quark = GLib.quark_to_string(domain)
+    return GError(message, domain_quark, code)
+
+
+# Monkey patch methods that rely on GLib introspection to be loaded at runtime.
+GError.matches = gerror_matches
+GError.new_literal = staticmethod(gerror_new_literal)
+
+
 __all__ += ['GError', 'OptionContext', 'OptionGroup', 'Pid',
             'spawn_async', 'threads_init']
 
diff --git a/gi/pygi-error.c b/gi/pygi-error.c
index 33275aa..d848a0e 100644
--- a/gi/pygi-error.c
+++ b/gi/pygi-error.c
@@ -314,16 +314,12 @@ pygi_arg_gerror_new_from_info (GITypeInfo   *type_info,
 void
 pygi_error_register_types (PyObject *module)
 {
-    PyObject *dict;
-    dict = PyDict_New();
-    /* This is a hack to work around the deprecation warning of
-     * BaseException.message in Python 2.6+.
-     * GError has also an "message" attribute.
-     */
-    PyDict_SetItemString(dict, "message", Py_None);
-    PyGError = PyErr_NewException("GLib.GError", PyExc_RuntimeError, dict);
-    Py_DECREF(dict);
-
-    PyModule_AddObject (module, "GError", PyGError);
+    PyObject *error_module = PyImport_ImportModule ("gi._error");
+    if (!error_module) {
+        return;
+    }
+
+    /* Stash a reference to the Python implemented gi._error.GError. */
+    PyGError = PyObject_GetAttrString (error_module, "GError");
 }
 
diff --git a/tests/test_error.py b/tests/test_error.py
index a022fdd..a509b8a 100644
--- a/tests/test_error.py
+++ b/tests/test_error.py
@@ -5,6 +5,7 @@
 #
 # Copyright (C) 2012 Will Thompson
 # Copyright (C) 2013 Martin Pitt
+# Copyright (C) 2014 Simon Feltman <sfeltman gnome org>
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -28,6 +29,42 @@ from gi.repository import GLib
 from gi.repository import GIMarshallingTests
 
 
+class TestType(unittest.TestCase):
+    def test_attributes(self):
+        e = GLib.GError('test message', 'mydomain', 42)
+        self.assertEqual(e.message, 'test message')
+        self.assertEqual(e.domain, 'mydomain')
+        self.assertEqual(e.code, 42)
+
+    def test_new_literal(self):
+        mydomain = GLib.quark_from_string('mydomain')
+        e = GLib.GError.new_literal(mydomain, 'test message', 42)
+        self.assertEqual(e.message, 'test message')
+        self.assertEqual(e.domain, 'mydomain')
+        self.assertEqual(e.code, 42)
+
+    def test_matches(self):
+        mydomain = GLib.quark_from_string('mydomain')
+        notmydomain = GLib.quark_from_string('notmydomain')
+        e = GLib.GError('test message', 'mydomain', 42)
+        self.assertTrue(e.matches(mydomain, 42))
+        self.assertFalse(e.matches(notmydomain, 42))
+        self.assertFalse(e.matches(mydomain, 40))
+
+    def test_str(self):
+        e = GLib.GError('test message', 'mydomain', 42)
+        self.assertEqual(str(e),
+                         'mydomain: test message (42)')
+
+    def test_repr(self):
+        e = GLib.GError('test message', 'mydomain', 42)
+        self.assertEqual(repr(e),
+                         "GLib.GError('test message', 'mydomain', 42)")
+
+    def test_inheritance(self):
+        self.assertTrue(issubclass(GLib.GError, RuntimeError))
+
+
 class TestMarshalling(unittest.TestCase):
     def test_array_in_crash(self):
         # Previously there was a bug in invoke, in which C arrays were unwrapped


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