[pygobject] Move property install function into propertyhelper.py



commit 438d3e68f19e2af5d027e18842ab05e0421d088d
Author: Simon Feltman <sfeltman src gnome org>
Date:   Sat Oct 20 19:56:04 2012 -0700

    Move property install function into propertyhelper.py
    
    Move _install_properties() into gi/_gobject/propertyhelper.py
    and add unittests.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=686559

 gi/_gobject/__init__.py       |   45 ++---------------------------
 gi/_gobject/propertyhelper.py |   45 +++++++++++++++++++++++++++++
 tests/test_properties.py      |   63 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 111 insertions(+), 42 deletions(-)
---
diff --git a/gi/_gobject/__init__.py b/gi/_gobject/__init__.py
index 7b6ab36..f12d3fe 100644
--- a/gi/_gobject/__init__.py
+++ b/gi/_gobject/__init__.py
@@ -30,7 +30,7 @@ if 'gobject' in sys.modules:
 from .. import _glib
 from . import _gobject
 from . import constants
-from .propertyhelper import Property
+from . import propertyhelper
 from . import signalhelper
 
 GBoxed = _gobject.GBoxed
@@ -211,6 +211,7 @@ G_MAXSSIZE = constants.G_MAXSSIZE
 G_MINOFFSET = constants.G_MINOFFSET
 G_MAXOFFSET = constants.G_MAXOFFSET
 
+Property = propertyhelper.Property
 Signal = signalhelper.Signal
 SignalOverride = signalhelper.SignalOverride
 
@@ -222,50 +223,10 @@ class GObjectMeta(type):
     "Metaclass for automatically registering GObject classes"
     def __init__(cls, name, bases, dict_):
         type.__init__(cls, name, bases, dict_)
-        cls._install_properties()
+        propertyhelper.install_properties(cls)
         signalhelper.install_signals(cls)
         cls._type_register(cls.__dict__)
 
-    def _install_properties(cls):
-        gproperties = getattr(cls, '__gproperties__', {})
-
-        props = []
-        for name, prop in cls.__dict__.items():
-            if isinstance(prop, Property):  # not same as the built-in
-                if name in gproperties:
-                    raise ValueError
-                prop.name = name
-                gproperties[name] = prop.get_pspec_args()
-                props.append(prop)
-
-        if not props:
-            return
-
-        cls.__gproperties__ = gproperties
-
-        if 'do_get_property' in cls.__dict__ or 'do_set_property' in cls.__dict__:
-            for prop in props:
-                if prop.fget != prop._default_getter or prop.fset != prop._default_setter:
-                    raise TypeError(
-                        "GObject subclass %r defines do_get/set_property"
-                        " and it also uses a property with a custom setter"
-                        " or getter. This is not allowed" % (
-                        cls.__name__,))
-
-        def obj_get_property(self, pspec):
-            name = pspec.name.replace('-', '_')
-            prop = getattr(cls, name, None)
-            if prop:
-                return prop.fget(self)
-        cls.do_get_property = obj_get_property
-
-        def obj_set_property(self, pspec, value):
-            name = pspec.name.replace('-', '_')
-            prop = getattr(cls, name, None)
-            if prop:
-                prop.fset(self, value)
-        cls.do_set_property = obj_set_property
-
     def _type_register(cls, namespace):
         ## don't register the class if already registered
         if '__gtype__' in namespace:
diff --git a/gi/_gobject/propertyhelper.py b/gi/_gobject/propertyhelper.py
index 82b06b0..a1e82c0 100644
--- a/gi/_gobject/propertyhelper.py
+++ b/gi/_gobject/propertyhelper.py
@@ -343,3 +343,48 @@ class Property(object):
             raise NotImplementedError(ptype)
 
         return (self.type, self.nick, self.blurb) + args + (self.flags,)
+
+
+def install_properties(cls):
+    """
+    Scans the given class for instances of Property and merges them
+    into the classes __gproperties__ dict if it exists or adds it if not.
+    """
+    gproperties = getattr(cls, '__gproperties__', {})
+
+    props = []
+    for name, prop in cls.__dict__.items():
+        if isinstance(prop, Property):  # not same as the built-in
+            if name in gproperties:
+                raise ValueError('Property %s was already found in __gproperties__' % name)
+            prop.name = name
+            gproperties[name] = prop.get_pspec_args()
+            props.append(prop)
+
+    if not props:
+        return
+
+    cls.__gproperties__ = gproperties
+
+    if 'do_get_property' in cls.__dict__ or 'do_set_property' in cls.__dict__:
+        for prop in props:
+            if prop.fget != prop._default_getter or prop.fset != prop._default_setter:
+                raise TypeError(
+                    "GObject subclass %r defines do_get/set_property"
+                    " and it also uses a property with a custom setter"
+                    " or getter. This is not allowed" % (
+                    cls.__name__,))
+
+    def obj_get_property(self, pspec):
+        name = pspec.name.replace('-', '_')
+        prop = getattr(cls, name, None)
+        if prop:
+            return prop.fget(self)
+    cls.do_get_property = obj_get_property
+
+    def obj_set_property(self, pspec, value):
+        name = pspec.name.replace('-', '_')
+        prop = getattr(cls, name, None)
+        if prop:
+            prop.fset(self, value)
+    cls.do_set_property = obj_set_property
diff --git a/tests/test_properties.py b/tests/test_properties.py
index 405375d..230ccd3 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -21,6 +21,7 @@ from gi.repository.GObject import \
 from gi.repository import Gio
 from gi.repository import GLib
 from gi.repository import GIMarshallingTests
+from gi._gobject import propertyhelper
 
 if sys.version_info < (3, 0):
     TEST_UTF8 = "\xe2\x99\xa5"
@@ -722,5 +723,67 @@ class TestProperty(unittest.TestCase):
 
         self.assertRaises(TypeError, tester._type_from_python, types.CodeType)
 
+
+class TestInstallProperties(unittest.TestCase):
+    # These tests only test how signalhelper.install_signals works
+    # with the __gsignals__ dict and therefore does not need to use
+    # GObject as a base class because that would automatically call
+    # install_signals within the meta-class.
+    class Base(object):
+        __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
+
+    class Sub1(Base):
+        pass
+
+    class Sub2(Base):
+        @GObject.Property(type=int)
+        def sub2test(self):
+            return 123
+
+    class ClassWithPropertyAndGetterVFunc(object):
+        @GObject.Property(type=int)
+        def sub2test(self):
+            return 123
+
+        def do_get_property(self, name):
+            return 321
+
+    class ClassWithPropertyRedefined(object):
+        __gproperties__ = {'test': (0, '', '', 0, 0, 0, 0)}
+        test = GObject.Property(type=int)
+
+    def setUp(self):
+        propertyhelper.install_properties(self.Base)
+
+    def test_subclass_without_properties_is_not_modified(self):
+        self.assertFalse('__gproperties__' in self.Sub1.__dict__)
+        propertyhelper.install_properties(self.Sub1)
+        self.assertFalse('__gproperties__' in self.Sub1.__dict__)
+
+    def test_subclass_with_decorator_gets_gproperties_dict(self):
+        # Sub2 has Property instances but will not have a __gproperties__
+        # until install_properties is called
+        self.assertFalse('__gproperties__' in self.Sub2.__dict__)
+        self.assertFalse('do_get_property' in self.Sub2.__dict__)
+        self.assertFalse('do_set_property' in self.Sub2.__dict__)
+
+        propertyhelper.install_properties(self.Sub2)
+        self.assertTrue('__gproperties__' in self.Sub2.__dict__)
+        self.assertEqual(len(self.Base.__gproperties__), 2)
+        self.assertEqual(len(self.Sub2.__gproperties__), 2)
+        self.assertTrue('sub2test' in self.Sub2.__gproperties__)
+
+        # get/set vfuncs should have been added
+        self.assertTrue('do_get_property' in self.Sub2.__dict__)
+        self.assertTrue('do_set_property' in self.Sub2.__dict__)
+
+    def test_object_with_property_and_do_get_property_vfunc_raises(self):
+        self.assertRaises(TypeError, propertyhelper.install_properties,
+                          self.ClassWithPropertyAndGetterVFunc)
+
+    def test_same_name_property_definitions_raises(self):
+        self.assertRaises(ValueError, propertyhelper.install_properties,
+                          self.ClassWithPropertyRedefined)
+
 if __name__ == '__main__':
     unittest.main()



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