Observable Properties

Observable Properties (OP) is a powerful mechanism which implement the Observer Pattern.

The mechanism which allows a part of the data contained in models to be observed by entities called Observers. It is fully automatic, as its management is carried out transparently by the base class Model.

Note:This section mainly focuses on OP in Models. The use of Observer here is anticipated only for examples. The section Observers presents full details about them.

Here a quick example is shown here, later all details are presented.

from gtkmvc import Model, Observer
# ----------------------------
class MyModel (Model):
   name = "Roberto"
   __observables__ = ("name",)
   pass # end of class
# ----------------------------
class MyObserver (Observer):
   @Observer.observe("name", assign=True)
   def notification(self, model, name, info):
       print "'name' changed from", info.old, "to", info.new
       return
   pass # end of class
# ----------------------------
m = MyModel()
o = MyObserver(m)
m.name = "Tobias"

In models, OPs are declared explicitly, and in observers we define methods which will be called when OPs are changed. In the example, when m.name is changed in the last line, method MyObserver.notification is called automatically to notify the observer. All details about observers will be presented in section Observers, in particular about what assign=True means.

Controllers are also observers, so the OP pattern clicks well with the MVC pattern.

If the example looks smooth and relatively easy, the topic is much more complex. OPs can be concrete or logical, and can be values (like in the previous example), or complex objects like containers or user’s classes. All these differences add complexity which will be described in details in the following sections.

Concrete Observable Properties

Concrete OPs have values stored inside the model. They are different from Logical OP whose values are calculated, or come from outside (e.g. from a database).

Values of OP are declared as class attributes in models, and OP names are declared in a special class member __observables__.

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"
   secondname = 'Mario'
   surname = "Cavada"
   energy = 0.2 # needs holidays!
   status = "sleepy"

   __observables__ = "name surname energy".split()
   pass # end of class

In the example, name, surname and energy are all observable, whereas status is not observable.

Special member __observables__ is a tuple (or a list) of names of class attributes that has to be observable. Names can contain wildcards like * to match any sequence of characters, ? to match one single character, etc. See module fnmatch in Python library for other information about possible use of wildcards in names. Important to say that if wildcards are used, attribute names starting with a double underscore __ will be not matched.

Note

It is also possible (but deprecated!) for the user to add a class variable called __properties__. This variable must be a map, whose elements’ keys are names of properties, and the associated values are the initial values. Using __properties__ is complementary to the use of __observables__, but it is provided for backward compatibility and should be not used in new code. This is an example of usage of deprecated __properties__, but you will not find another in this manual:

from gtkmvc import Model
class MyModelDeprecated (Model):
      __properties__ = {
      'name' : 'Rob',
      }
      pass # end of class

This is another example showing the usage of wildcards in names:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"
   secondname = 'Mario'
   surname = "Cavada"
   energy = 0.2 # needs holidays!
   entropy = 1.0
   enology = "good science"
   status = "sleepy"

   __observables__ = ("*name", "en????y")
   pass # end of class

In the example, all attributes but energy and status are declared to be observable.

Concrete OP and inheritance

Things so far are easy enough, but they get a bit complicated when you derive custom models from other custom models. For example, what happens to OP if you derive a new model class from the class MyModel?

In this case the behavior of the OP trusty follows the typical Object Oriented rules:

  • Any concrete OP in base class are inherited by derived classes.
  • Derived class can override any concrete OP in base classes.
  • If multiple base classes defines the same OP, only the first OP will be accessible from the derived class.

For example:

from gtkmvc import Model

class Base (Model):
   prop1 = 1
   __observables__ = ("prop1", )

   def __init__(self):
       Model.__init__(self)

        # this class is an observer of its own properties:
       self.register_observer(self)
       return

   @Model.observe("prop1", assign=True)
   def prop1_changed(self, model, name, info):
       print model, "prop1 changed from '%s' to '%s'" % (info.old, info.new)
       return
   pass # end of class
# --------------------------------------------------------

class Der (Base):
   prop2 = 2
   __observables__ = ("prop2",)

   @Model.observe("prop2", assign=True)
   def prop2_changed(self, model, name, info):
       print self, "prop2 changed from '%s' to '%s'" % (info.old, info.new)
       return
   pass # end of class
# --------------------------------------------------------

# test code:
b = Base()
d = Der()

d.prop2 *= 10
d.prop1 *= 10
b.prop1 *= 10

When executed, this script generates this output:

<__main__.Der object  ...> prop2 changed from '2' to '20'
<__main__.Der object  ...> prop1 changed from '1' to '10'
<__main__.Base object ...> prop1 changed from '1' to '10'

Let’s analyse the example.

First, in the Base.__init__ constructor you can see that the instance registers itself as an observer... of itself! As we will see in section Observers, class Model derives from Observer, so all models are also observer.

In the example this is exploited only to write a compact example (it is not needed to define an additional class for the observer). However, in complex designs it is quite common to see models observing them self, or sub-models contained inside them.

Second, method Base.prop1_changed is explicitly marked to observe property prop1.

Third, in class Der only the OP prop2 is declared, as prop1 is inherited from class Base. This is clearly visible in the output:

<__main__.Der object  ...> prop1 changed from '1' to '10'

It is possible to change type and default values of OPs in derived class, by re-declaring the OSs. For example:

class Der (Base):
   prop1 = 3
   prop2 = 2
   __observables__ = ("prop?",)

   @Observer.observe("prop2", assign=True)
   def prop2_changed(self, model, name, info):
       print self, "prop2 changed from '%s' to '%s'" % (info.old, info.new)
       return
   pass # end of class
# --------------------------------------------------------

This would produce the output:

<__main__.Der object  ...> prop2 changed from '2' to '20'
<__main__.Der object  ...> prop1 changed from '3' to '30'
<__main__.Base object ...> prop1 changed from '1' to '10'

As you can see, d.prop1 overrides the OP prop1 defined in Base (they have different initial values now).

Logical Observable Properties

Logical OPs are properties whose values are not necessarily stored in the model, but which are read and written by a pair of getter/setter methods.

This make logical OPs ideal for:

  • Values calculated out of other OPs (which can be either logical or concrete).
  • Values living outside the model, like e.g. in a database, or in a remote server.

Logical OPs are declared like concrete OPs, but no correspoding attributes have to appear in the class. Their name have to appear only within the special member __observables__. For example:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"

   __observables__ = ("name", "happiness")
   pass # end of class
# ----------------------------

In the example, name is a concrete property, whereas happiness is a logical property, as no corresponding attribute exists in class MyModel.

Note

Notice that names of logical OPs occurring within the special member __observables__ cannot contain wildcards like concrete properties.

The reasons for this limitation is obvious, as wildcards can be used to match only class attributes.)

However, a logical OP’s value is taken from a getter method, and for a read/write OP the values are stored through a setter method. Defining a getter is mandatory, while defining a setter is required only for writable logical OPs.

getter/setter methods can be defined by exploiting decorators, or by exploiting a naming convention.

Use of decorators for defining getters and/or setters

Decorators @Model.getter and @Model.setter can be used for defining logical OPs getter and setter respectively. The syntax and semantics are very similar to the python @property decorator.

E.g. for logical OP happiness in the previous example:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"

   __observables__ = ("name", "happiness")

   _a_value = 1.0 # support for happiness
   @Model.getter
   def happiness(self): return self._a_value

   @Model.setter
   def happiness(self, value): self._a_value = max(1.0, value)

   pass # end of class
# ----------------------------

It is possible to define getter/setter methods which serve multiple logical OPs. For example:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"

   __observables__ = ("name", "happiness", "energy")

   _a_value = 1.0 # support for happiness
   @Model.getter("happiness", "energy")
   def a_getter_for_several_ops(self, name):
     if "energy" == name: return 0.1 # constantly need holidays!
     return self._a_value

   @Model.setter
   def happiness(self, value): self._a_value = max(1.0, value)

   pass # end of class
# ----------------------------

In the example, the decorator @Model.getter is used with arguments, which have to be the string names of all properties which have to be handled by the decorated method. The method (the getter in this case) will receive the name of the property along with its other arguments.

Use of wildcards is allowed in decorators names, and will match all logical OPs not exactly matched by other decorators. It is an error condition if multiple matches are found when matching logical OPs specified with wildcards. For example this is perfectly legal:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"

   __observables__ = ("name", "energy", "entropy", "enology")

   @Model.getter
   def energy(self): return 0.1  # constantly need holidays!

   @Model.getter("enology")
   def getter1(self, name): return "good science!"

   @Model.getter("en*") # matches only remaining 'entropy'
   def getter2(self, name):
     assert "entropy" == name
     return 0

   @Model.setter("*") # matches "energy", "entropy", "enology"
   def happiness(self, name, value):
       print "setter for", name, value
       ...
       return


   pass # end of class
# ----------------------------

However, this example is not legal:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):

   __observables__ = ("energy", "entropy", "enology")

   @Model.getter("en*") # matches energy, entropy, and enology
   def getter1(self, name): ...

   @Model.getter("*") # matches energy, entropy, and enology
   def getter2(self, name): ...

   pass # end of class
# ----------------------------

The example does not work as ambiguity is found when resolving wilcards.

Use of naming convention for defining getters and/or setters

In some cases, the use of decorators for defining getters/setters can be a limitation. For example, when the model is built dynamically, like when generating proxy classes.

In these and other cases, the framework supports a naming convention which can be used to define implicitly getters and/or setters for logical OPs.

The naming convention applies to Model’s method names which are implicitly declared as getters or setters.

  • get_<prop_name>_value(self): A specific getter for OP <prop_name>.
  • set_<prop_name>_value(self, value): A specific setter for OP <prop_name>.
  • get__value(self, name): A generic getter receiving the name of the property to be get.
  • set__value(self, name, value): A generic setter receiving the name of the property to be set.

As you see getters/setters can be either specific or generic. In the former case, the getter/setter is specific for one OP. In the latter case, getter/setter is general and will receive the name of the property.

Generic getter/setter will not be called for OPs which have specific getter/setter defined. For example:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   __observables__ = ("energy", "entropy", "enology")

   def get_energy_value(self): return 0.1  # constantly need holidays!

   # getter for entropy and enology only, as energy has a specific getter
   def get__value(self, name): ...

   # setter for all properties
   def set_value(self, name, value): ...

   pass # end of class
# ----------------------------

The first example we presented for decorators could be rewritten as:

from gtkmvc import Model
# ----------------------------
class MyModel (Model):
   name = "Roberto"

   __observables__ = ("name", "energy", "entropy", "enology")

   def get_energy_value(self): return 0.1  # constantly need holidays!

   def get_enology_value(self): return "good science!"

   def get__value(self, name):
     assert "entropy" == name
     return 0

   def set__value(self, name, value):
       print "setter for", name, value
       ...
       return

   pass # end of class
# ----------------------------

Of course, since in naming conventions names matters, some names in the example had to be adapted.

Types of Observable Properties

In section Supported types of Observable Properties we anticipated that there exist several types of OP*s. In the examples so far we have seen only *value OPs, meaning that observers will be notified of any change of value, i.e. when the OPs are assigned to different values [1].

What would happen if the value of the property would be a complex object like a list, or a user-defined class, and the object would change internally?

For example:

from gtkmvc import Model

class MyModel (Model):
    prop1 = [1,2,3]
    __observables__ = ("prop1",)

    def __init__(self):
        Model.__init__(self)
        ...
        return
    pass # end of class

m = MyModel()
m.prop1.append(4)
m.prop1[1] = 5

Last two lines of the previous example actually change the OP internally, that is different from assigning a new value to the property like in m.prop1 = [5,4,3,2] that would trigger an assign notifications like those seen in previous examples. Similar problem is found when the property is assigned to a class instance, and then a method that change the instance is called.

Supported objects

In this section only the model-side will be presented. In the section dedicated to observers, we will see how changes occurring to this objects are notified.

Mutable containers

The framework MVC-O provides a full support for python mutable containers like lists and maps. For example:

from gtkmvc import Model, Observer

# ----------------------------------------------------------------------
class MyModel (Model):
    myint = 0
    mylist = []
    mymap = {}
    __observables__ = ("my*", )

    pass # end of class

This covers those cases where you have your OPs holding mutable sequence values.

Class Instances

What if the value is a user-defined class instance? Suppose a class has a method changing the content instance. The idea is that observers are notified when the method is called, with the possibility to choose if being notified before or after the call.

However, how can the user declare that method M does changes the instance? Two mechanism are provided by the framework:

  • For already existing classes. In this cases the declaration occurs when the instance is assigned to the OP in the model.
  • For ad-hoc and new classes. In this case the method will be declared as Observable at the class level, through a special decorator provided by the framework. This is the preferable manner.

Examples for new classes:

from gtkmvc import Model, Observable

# ----------------------------------------------------------------------
class AdHocClass (Observable):
    def __init__(self):
        Observable.__init__(self)
        self.val = 0
        return

    # this way the method is declared as 'observed':
    @Observable.observed
    def change(self): self.val += 1

    # this is NOT observed (and it does not change the instance):
    def is_val(self, val): return self.val == val
    pass #end of class

# ----------------------------------------------------------------------
class MyModel (Model):
    obj = AdHocClass()
    __observables__ = ("obj",)

    pass # end of class

As you can see, declaring a class as observable is as simple as deriving from gtkmvc.Observable and decorating those class methods that must be observed with the decorator gtkmvc.Observable.observed (decorators are supported by Python version 2.4 and later only).

However, sometimes we want to reuse existing classes and it is not possible to derive them from gtkmvc.Observable. In this case declaration of the methods to be observed can be done at time of declaration of the corresponding OP. In this case the value to be assigned to the OP must be a triple (class, instance, method_names>, where:

class
Is the class of the object to be observed.
instance
Is the object to be observed.
method_names
Is a tuple of strings, representing the method names of the instance to be observed.

For example:

from gtkmvc import Model
#----------------------------------------------------------------------
# This is a class the used cannot/don't want to change
class HolyClass (object):
    def __init__(self): self.val = 0
    def change(self): self.val += 1
    pass #end of class


# ----------------------------------------------------------------------
class MyModel (Model):
    obj = (HolyClass, HolyClass(), ('change',))
    __observables__ = ("obj",)

    pass # end of class

Signals

Finally, OP can hold special values that are signals that can be used to notify observers that certain events occurred.

To declare an OP as a signal, the value of the OP must be an instance of class gtkmvc.Signal. To notify an event, the model can then invoke method emit of the OP. Emitting a signal can carry an optional argument.

For example:

from gtkmvc import Model, Signal

# ----------------------------------------------------------------------
class MyModel (Model):
    sgn = Signal()
    __observables__ = ("sgn",)

    pass

if __name__ == "__main__":
    m = MyModel()
    m.sgn.emit() # we emit a signal
    m.sgn.emit("hello!") # with argument
    pass

In the examples, there are several examples that show how different types of OPs can be used. Of course all available types can be used in all available kind of model classes, with or without multi-threading support.

Class vs Instance members as OPs

So far in our examples, all OPs were class members:

from gtkmvc import Model

class MyModel (Model):
    prop1 = 10
    prop2 = []
    __observables__ = ("prop?",)
    pass # end of class

Using class vs instance attributes is not an issue when they are assigned:

m1 = MyModel()
m2 = MyModel()
m1.prop1 = 5
m2.prop1 = 15

In this case after the assignment m1 and m2 will have their own value for attribute prop1.

However, when dealing with attributes whose type is a class instances, like for example a list, you must keep in mind the attribute sharing.

m1.prop2.append(1)
print m2.prop2 # prints [1] !

If attribute sharing is not what you want, simply assign OPs in the model’s constructor:

class MyModel (Model):
    prop1 = 10
    prop2 = [] # may be any value actually
    __observables__ = ("prop?",)

    def __init__(self):
      MyModel.__init__(self)
      self.prop2 = []
      return
    pass # end of class

Now m1.prop2 and m2.prop2 are different objects, and sharing no longer occurs.

Section Notes

[1]Actually there exist spurious assign notifications, which are issued also when there is no change in the value of an OP, e.g. when an OP is assigned to itself.

Error and Warning conditions with logical OPs

When dealing with logical OPs and in particular with getter/setter pairs, there are complex conditions and behaviours of the framework which may be confusing. Here those conditions are listed and explained.

Error conditions

  • A logical OP has no associated getter.

    Getters are mandatory for logical OPs.

  • OP matched by multiple getters/setters.

    Each OP must be matched exactly with one getter and optionally with one setter. It is an error condition any multiple matching.

  • Exactly the same pattern was used for one or more getters (setters).

    This is a specialization of previous case (multiple matching).

Warning conditions

Warning are issued only if the devlopers enables them. In released applications, warnings should be not visible by default, to avoid worrying the application user.

When developing, it is important to enable warnings, though.

  • A setter has no corresponing getter.

    When a setter has no getter, the setter is simply ignored.

  • Getter/setter defined for concrete OPs.

    When a getter and/or a setter is defined for a concrete OP (not a logical OP), the getter/setter are ignored.

  • Getter/setter defined for non-existing logical OP.

    Getters/setters whose pattern do not match any existing logical OP, are ignored.

Special members for Observable Properties

Classes derived from Model, that exports OPs, have several special members. Advanced users might be interested in overriding some of them, but in general they should be considered as private members. They are explained here for the sake of completeness.

__observables__
A class (static) member that lists property names. This must be provided as either a tuple or a list by the user. Wilcards in names can be used to match property names, but properties with names starting with a double underscore __ will be not matched.
__properties__
(Deprecated, do not use anymore) A dictionary mapping observable properties names and their initial value. This variable has been substituted by __observables__.
__derived_properties__
(Deprecated) Automatically generated static member that maps the OPs exported by all base classes. This does not contain OPs that the class overrides.
_prop_<property_name>
This is an auto-generated variable holding the property value. For example, a property called x will generate a variable called _prop_x.
get_prop_<property_name>
This public method is the getter for the property. It is automatically generated only if the user does not define one. This means that the user can change the behavior of it by defining their own method. For example, for property x the method is get_prop_x. This method gets only self and returns the corresponding property value.
set_prop_<property_name>
This public method is customizable like get_prop_<property_name>. This does not return anything, and gets self and the value to be assigned to the property. The default auto-generated code also calls method gtkmvc.Model.notify_property_change to notify the change to all registered observers.

For further details about this topic see meta-classes PropertyMeta and ObservablePropertyMeta from package support.