[pygobject] Don't let Property.setter() method names define property names



commit 8117e6bce73581e89211371708ff7d5de7d870d4
Author: Martin Pitt <martinpitt gnome org>
Date:   Thu Jan 10 12:13:16 2013 +0100

    Don't let Property.setter() method names define property names
    
    Defining property names in install_properties() is too late when using
    @propname.setter decorators; their method names don't define a property name,
    nor are they even required to be a valid property identifier.
    
    So change the logic to already fix the property name when using a setter
    decorator and use that instead of the member name in install_properties().
    
    https://bugzilla.gnome.org/show_bug.cgi?id=688971

 gi/_gobject/propertyhelper.py |   29 ++++++++++++++++++++++-------
 tests/test_properties.py      |   18 ++++++++++++++++++
 2 files changed, 40 insertions(+), 7 deletions(-)
---
diff --git a/gi/_gobject/propertyhelper.py b/gi/_gobject/propertyhelper.py
index a038f1b..d5a1852 100644
--- a/gi/_gobject/propertyhelper.py
+++ b/gi/_gobject/propertyhelper.py
@@ -149,6 +149,8 @@ class Property(object):
         @keyword maximum:  maximum allowed value (int, float, long only)
         """
 
+        self.name = None
+
         if type is None:
             type = object
         self.type = self._type_from_python(type)
@@ -180,7 +182,9 @@ class Property(object):
             getter = self._default_getter
             setter = self._default_setter
         self.getter(getter)
-        self.setter(setter)
+        # do not call self.setter() here, as this defines the property name
+        # already
+        self.fset = setter
 
         if minimum is not None:
             if minimum < self._get_minimum():
@@ -199,8 +203,6 @@ class Property(object):
             maximum = self._get_maximum()
         self.maximum = maximum
 
-        self.name = None
-
         self._exc = None
 
     def __repr__(self):
@@ -248,6 +250,11 @@ class Property(object):
     def setter(self, fset):
         """Set the setter function to fset. For use as a decorator."""
         self.fset = fset
+        # with a setter decorator, we must ignore the name of the method in
+        # install_properties, as this does not need to be a valid property name
+        # and does not define the property name. So set the name here.
+        if not self.name:
+            self.name = self.fget.__name__
         return self
 
     def _type_from_python(self, type_):
@@ -364,10 +371,18 @@ def install_properties(cls):
     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()
+            # if a property was defined with a decorator, it may already have
+            # a name; if it was defined with an assignment (prop = Property(...))
+            # we set the property's name to the member name
+            if not prop.name:
+                prop.name = name
+            # we will encounter the same property multiple times in case of
+            # custom setter methods
+            if prop.name in gproperties:
+                if gproperties[prop.name] == prop.get_pspec_args():
+                    continue
+                raise ValueError('Property %s was already found in __gproperties__' % prop.name)
+            gproperties[prop.name] = prop.get_pspec_args()
             props.append(prop)
 
     if not props:
diff --git a/tests/test_properties.py b/tests/test_properties.py
index fe286e2..d19970f 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -541,6 +541,24 @@ class TestProperty(unittest.TestCase):
         self.assertEqual(o.value, 'blah')
         self.assertEqual(o.props.value, 'blah')
 
+    def test_decorator_private_setter(self):
+        class C(GObject.GObject):
+            _value = 'value'
+
+            @GObject.Property
+            def value(self):
+                return self._value
+
+            @value.setter
+            def _set_value(self, value):
+                self._value = value
+
+        o = C()
+        self.assertEqual(o.value, 'value')
+        o.value = 'blah'
+        self.assertEqual(o.value, 'blah')
+        self.assertEqual(o.props.value, 'blah')
+
     def test_decorator_with_call(self):
         class C(GObject.GObject):
             _value = 1



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