huge use of weakrefs to remove memoryleaks due to circular references
[tiramisu.git] / tiramisu / value.py
index cd57a4c..c654206 100644 (file)
@@ -19,6 +19,7 @@
 # ____________________________________________________________
 from time import time
 from copy import copy
+import weakref
 from tiramisu.error import ConfigError, SlaveError
 from tiramisu.setting import owners, multitypes, expires_time
 from tiramisu.autolib import carry_out_calculation
@@ -40,7 +41,7 @@ class Values(object):
         :param context: the context is the home config's values
 
         """
-        self.context = context
+        self.context = weakref.ref(context)
         # the storage type is dictionary or sqlite3
         import_lib = 'tiramisu.storage.{0}.value'.format(storage.storage)
         self._p_ = __import__(import_lib, globals(), locals(), ['Values'],
@@ -52,7 +53,7 @@ class Values(object):
 
         :param opt: the `option.Option()` object
         """
-        meta = self.context.cfgimpl_get_meta()
+        meta = self.context().cfgimpl_get_meta()
         if meta is not None:
             value = meta.cfgimpl_get_values()[opt]
         else:
@@ -105,10 +106,10 @@ class Values(object):
         if path is None:
             path = self._get_opt_path(opt)
         if self._p_.hasvalue(path):
-            setting = self.context.cfgimpl_get_settings()
-            opt.impl_validate(opt.impl_getdefault(), self.context,
+            setting = self.context().cfgimpl_get_settings()
+            opt.impl_validate(opt.impl_getdefault(), self.context(),
                               'validator' in setting)
-            self.context.cfgimpl_reset_cache()
+            self.context().cfgimpl_reset_cache()
             if (opt.impl_is_multi() and
                     opt.impl_get_multitype() == multitypes.master):
                 for slave in opt.impl_get_master_slaves():
@@ -136,7 +137,7 @@ class Values(object):
         callback, callback_params = opt._callback
         if callback_params is None:
             callback_params = {}
-        return carry_out_calculation(opt._name, config=self.context,
+        return carry_out_calculation(opt._name, config=self.context(),
                                      callback=callback,
                                      callback_params=callback_params,
                                      index=index)
@@ -160,7 +161,7 @@ class Values(object):
                 return value
         val = self._getitem(opt, path, validate, force_permissive, force_properties,
                             validate_properties)
-        if 'expire' in self.context.cfgimpl_get_settings() and validate and \
+        if 'expire' in self.context().cfgimpl_get_settings() and validate and \
                 validate_properties and force_permissive is False and \
                 force_properties is None:
             if ntime is None:
@@ -172,7 +173,7 @@ class Values(object):
     def _getitem(self, opt, path, validate, force_permissive, force_properties,
                  validate_properties):
         # options with callbacks
-        setting = self.context.cfgimpl_get_settings()
+        setting = self.context().cfgimpl_get_settings()
         is_frozen = 'frozen' in setting[opt]
         # if value is callback and is not set
         # or frozen with force_default_on_freeze
@@ -183,7 +184,7 @@ class Values(object):
             if (opt.impl_is_multi() and
                     opt.impl_get_multitype() == multitypes.slave):
                 masterp = self._get_opt_path(opt.impl_get_master_slaves())
-                mastervalue = getattr(self.context, masterp)
+                mastervalue = getattr(self.context(), masterp)
                 lenmaster = len(mastervalue)
                 if lenmaster == 0:
                     value = []
@@ -203,11 +204,11 @@ class Values(object):
         elif is_frozen and 'force_default_on_freeze' in setting[opt]:
             value = self._getdefault(opt)
             if opt.impl_is_multi():
-                value = Multi(value, self.context, opt, path, validate)
+                value = Multi(value, self.context(), opt, path, validate)
         else:
             value = self._getvalue(opt, path, validate)
         if validate:
-            opt.impl_validate(value, self.context, 'validator' in setting)
+            opt.impl_validate(value, self.context(), 'validator' in setting)
         if self._is_default_owner(path) and \
                 'force_store_value' in setting[opt]:
             self.setitem(opt, value, path, is_write=False)
@@ -225,8 +226,8 @@ class Values(object):
         # is_write is, for example, used with "force_store_value"
         # user didn't change value, so not write
         # valid opt
-        opt.impl_validate(value, self.context,
-                          'validator' in self.context.cfgimpl_get_settings())
+        opt.impl_validate(value, self.context(),
+                          'validator' in self.context().cfgimpl_get_settings())
         if opt.impl_is_multi() and not isinstance(value, Multi):
             value = Multi(value, self.context, opt, path)
         self._setvalue(opt, path, value, force_permissive=force_permissive,
@@ -235,14 +236,14 @@ class Values(object):
     def _setvalue(self, opt, path, value, force_permissive=False,
                   force_properties=None,
                   is_write=True, validate_properties=True):
-        self.context.cfgimpl_reset_cache()
+        self.context().cfgimpl_reset_cache()
         if validate_properties:
-            setting = self.context.cfgimpl_get_settings()
+            setting = self.context().cfgimpl_get_settings()
             setting.validate_properties(opt, False, is_write,
                                         value=value, path=path,
                                         force_permissive=force_permissive,
                                         force_properties=force_properties)
-        owner = self.context.cfgimpl_get_settings().getowner()
+        owner = self.context().cfgimpl_get_settings().getowner()
         self._p_.setvalue(path, value, owner)
 
     def getowner(self, opt):
@@ -259,7 +260,7 @@ class Values(object):
 
     def _getowner(self, path):
         owner = self._p_.getowner(path, owners.default)
-        meta = self.context.cfgimpl_get_meta()
+        meta = self.context().cfgimpl_get_meta()
         if owner is owners.default and meta is not None:
             owner = meta.cfgimpl_get_values()._getowner(path)
         return owner
@@ -311,7 +312,7 @@ class Values(object):
         :param opt: the `option.Option` object
         :returns: a string with points like "gc.dummy.my_option"
         """
-        return self.context.cfgimpl_get_description().impl_get_path_by_opt(opt)
+        return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt)
 
 # ____________________________________________________________
 # multi types
@@ -330,6 +331,8 @@ class Multi(list):
         """
         self.opt = opt
         self.path = path
+        if not isinstance(context, weakref.ReferenceType):
+            raise ValueError('context must be a Weakref')
         self.context = context
         if not isinstance(value, list):
             value = [value]
@@ -341,13 +344,13 @@ class Multi(list):
 
     def _valid_slave(self, value):
         #if slave, had values until master's one
-        masterp = self.context.cfgimpl_get_description().impl_get_path_by_opt(
+        masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt(
             self.opt.impl_get_master_slaves())
-        mastervalue = getattr(self.context, masterp)
+        mastervalue = getattr(self.context(), masterp)
         masterlen = len(mastervalue)
         valuelen = len(value)
         if valuelen > masterlen or (valuelen < masterlen and
-                                    not self.context.cfgimpl_get_values(
+                                    not self.context().cfgimpl_get_values(
                                     )._is_default_owner(self.path)):
             raise SlaveError(_("invalid len for the slave: {0}"
                                " which has {1} as master").format(
@@ -360,7 +363,7 @@ class Multi(list):
 
     def _valid_master(self, value):
         masterlen = len(value)
-        values = self.context.cfgimpl_get_values()
+        values = self.context().cfgimpl_get_values()
         for slave in self.opt._master_slaves:
             path = values._get_opt_path(slave)
             if not values._is_default_owner(path):
@@ -379,7 +382,7 @@ class Multi(list):
         self._validate(value)
         #assume not checking mandatory property
         super(Multi, self).__setitem__(key, value)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
 
     def append(self, value, force=False):
         """the list value can be updated (appened)
@@ -390,7 +393,7 @@ class Multi(list):
                 raise SlaveError(_("cannot append a value on a multi option {0}"
                                    " which is a slave").format(self.opt._name))
             elif self.opt.impl_get_multitype() == multitypes.master:
-                values = self.context.cfgimpl_get_values()
+                values = self.context().cfgimpl_get_values()
                 if value is None and self.opt.impl_has_callback():
                     value = values._getcallback_value(self.opt)
                     #Force None il return a list
@@ -398,7 +401,7 @@ class Multi(list):
                         value = None
         self._validate(value)
         super(Multi, self).append(value)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
         if not force and self.opt.impl_get_multitype() == multitypes.master:
             for slave in self.opt.impl_get_master_slaves():
                 path = values._get_opt_path(slave)
@@ -425,7 +428,7 @@ class Multi(list):
             raise SlaveError(_("cannot sort multi option {0} if master or slave"
                                "").format(self.opt._name))
         super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
 
     def reverse(self):
         if self.opt.impl_get_multitype() in [multitypes.slave,
@@ -433,7 +436,7 @@ class Multi(list):
             raise SlaveError(_("cannot reverse multi option {0} if master or "
                                "slave").format(self.opt._name))
         super(Multi, self).reverse()
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
 
     def insert(self, index, obj):
         if self.opt.impl_get_multitype() in [multitypes.slave,
@@ -441,7 +444,7 @@ class Multi(list):
             raise SlaveError(_("cannot insert multi option {0} if master or "
                                "slave").format(self.opt._name))
         super(Multi, self).insert(index, obj)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
 
     def extend(self, iterable):
         if self.opt.impl_get_multitype() in [multitypes.slave,
@@ -449,7 +452,7 @@ class Multi(list):
             raise SlaveError(_("cannot extend multi option {0} if master or "
                                "slave").format(self.opt._name))
         super(Multi, self).extend(iterable)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
 
     def _validate(self, value):
         if value is not None:
@@ -474,7 +477,7 @@ class Multi(list):
                                    " which is a slave").format(self.opt._name))
             elif self.opt.impl_get_multitype() == multitypes.master:
                 for slave in self.opt.impl_get_master_slaves():
-                    values = self.context.cfgimpl_get_values()
+                    values = self.context().cfgimpl_get_values()
                     if not values.is_default_owner(slave):
                         #get multi without valid properties
                         values.getitem(slave,
@@ -482,5 +485,5 @@ class Multi(list):
                                        ).pop(key, force=True)
         #set value without valid properties
         ret = super(Multi, self).pop(key)
-        self.context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
+        self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
         return ret