require works well in sqlalchemy storage
This commit is contained in:
parent
d3f42efe85
commit
a1dd2cfce7
|
@ -311,10 +311,10 @@ def test_append_properties():
|
|||
cfg = Config(descr)
|
||||
setting = cfg.cfgimpl_get_settings()
|
||||
option = cfg.cfgimpl_get_description().gc.dummy
|
||||
assert tuple(option.impl_getproperties()) == tuple()
|
||||
assert tuple(option._properties) == tuple()
|
||||
assert not 'test' in setting[option]
|
||||
setting[option].append('test')
|
||||
assert tuple(option.impl_getproperties()) == tuple()
|
||||
assert tuple(option._properties) == tuple()
|
||||
assert 'test' in setting[option]
|
||||
|
||||
|
||||
|
|
|
@ -67,11 +67,9 @@ class Base(StorageBase):
|
|||
raise ValueError(_("invalid name: {0} for option").format(name))
|
||||
self._name = name
|
||||
self.impl_set_information('doc', doc)
|
||||
requires = validate_requires_arg(requires, self._name)
|
||||
if requires is not None:
|
||||
for values in requires.values():
|
||||
for require in values.values():
|
||||
self._add_require(require)
|
||||
self._calc_properties, self._requires = validate_requires_arg(
|
||||
requires, self._name)
|
||||
if not multi and default_multi is not None:
|
||||
raise ValueError(_("a default_multi is set whereas multi is False"
|
||||
" in option: {0}").format(name))
|
||||
|
@ -118,8 +116,8 @@ class Base(StorageBase):
|
|||
if callback_params is not None:
|
||||
for key, values in callback_params.items():
|
||||
self._add_callback(key, values)
|
||||
if requires is not None and properties is not tuple():
|
||||
set_forbidden_properties = set(properties) & set(requires.keys())
|
||||
if self._calc_properties != frozenset([]) and properties is not tuple():
|
||||
set_forbidden_properties = self._calc_properties & set(properties)
|
||||
if set_forbidden_properties != frozenset():
|
||||
raise ValueError('conflict: properties already set in '
|
||||
'requirement {0}'.format(
|
||||
|
@ -133,8 +131,9 @@ class Base(StorageBase):
|
|||
self._default = []
|
||||
else:
|
||||
self._default = default
|
||||
for prop in properties:
|
||||
self._properties.append(self._get_property_object(prop))
|
||||
self._properties = properties
|
||||
#for prop in properties:
|
||||
#self._properties.append(self._get_property_object(prop))
|
||||
self._warnings_only = warnings_only
|
||||
return super(Base, self).__init__()
|
||||
|
||||
|
@ -148,6 +147,29 @@ class BaseOption(Base):
|
|||
# '_calc_properties', '_impl_informations',
|
||||
# '_state_readonly', '_state_requires', '_stated')
|
||||
|
||||
# information
|
||||
def impl_set_information(self, key, value):
|
||||
"""updates the information's attribute
|
||||
(which is a dictionary)
|
||||
|
||||
:param key: information's key (ex: "help", "doc"
|
||||
:param value: information's value (ex: "the help string")
|
||||
"""
|
||||
self._informations[key] = value
|
||||
|
||||
def impl_get_information(self, key, default=None):
|
||||
"""retrieves one information's item
|
||||
|
||||
:param key: the item string (ex: "help")
|
||||
"""
|
||||
if key in self._informations:
|
||||
return self._informations[key]
|
||||
elif default is not None:
|
||||
return default
|
||||
else:
|
||||
raise ValueError(_("information's item not found: {0}").format(
|
||||
key))
|
||||
|
||||
# ____________________________________________________________
|
||||
# serialize object
|
||||
def _impl_convert_requires(self, descr, load=False):
|
||||
|
@ -349,9 +371,9 @@ class Option(BaseOption):
|
|||
# name))
|
||||
# object.__setattr__(self, name, value)
|
||||
|
||||
def impl_getproperties(self):
|
||||
for prop in self._properties:
|
||||
yield(prop.name)
|
||||
#def impl_getproperties(self):
|
||||
# for prop in self._properties:
|
||||
# yield(prop.name)
|
||||
|
||||
def impl_getrequires(self):
|
||||
return self._requires
|
||||
|
@ -1161,10 +1183,10 @@ class OptionDescription(BaseOption, StorageOptionDescription):
|
|||
# the group_type is useful for filtering OptionDescriptions in a config
|
||||
self._optiondescription_group_type = groups.default
|
||||
|
||||
def impl_getproperties(self):
|
||||
#FIXME
|
||||
for prop in self._properties:
|
||||
yield(prop.name)
|
||||
#def impl_getproperties(self):
|
||||
# #FIXME
|
||||
# for prop in self._properties:
|
||||
# yield(prop.name)
|
||||
|
||||
def impl_getrequires(self):
|
||||
#FIXME
|
||||
|
@ -1223,14 +1245,16 @@ class OptionDescription(BaseOption, StorageOptionDescription):
|
|||
#for child in self._children:
|
||||
# yield(session.query(child._type).filter_by(id=child.id).first())
|
||||
|
||||
def impl_build_cache_consistency(self, _consistencies=None):
|
||||
def impl_build_cache_consistency(self, _consistencies=None, cache_option=None):
|
||||
#FIXME cache_option !
|
||||
if _consistencies is None:
|
||||
init = True
|
||||
_consistencies = {}
|
||||
cache_option = []
|
||||
else:
|
||||
init = False
|
||||
for option in self.impl_getchildren():
|
||||
cache_option.append(option.id)
|
||||
if not isinstance(option, OptionDescription):
|
||||
for consistency in option._consistencies:
|
||||
func = consistency.func
|
||||
|
@ -1240,15 +1264,15 @@ class OptionDescription(BaseOption, StorageOptionDescription):
|
|||
[]).append((func,
|
||||
all_cons_opts))
|
||||
else:
|
||||
option.impl_build_cache_consistency(_consistencies)
|
||||
option.impl_build_cache_consistency(_consistencies, cache_option)
|
||||
if init and _consistencies != {}:
|
||||
self._cache_consistencies = {}
|
||||
for opt, cons in _consistencies.items():
|
||||
#FIXME dans le cache ...
|
||||
#if opt.id not in cache_option:
|
||||
# raise ConfigError(_('consistency with option {0} '
|
||||
# 'which is not in Config').format(
|
||||
# opt.impl_getname()))
|
||||
if opt.id not in cache_option:
|
||||
raise ConfigError(_('consistency with option {0} '
|
||||
'which is not in Config').format(
|
||||
opt.impl_getname()))
|
||||
self._cache_consistencies[opt] = tuple(cons)
|
||||
|
||||
def impl_validate_options(self, cache_option=None):
|
||||
|
@ -1509,15 +1533,15 @@ def validate_requires_arg(requires, name):
|
|||
inverse, transitive, same_action)
|
||||
else:
|
||||
ret_requires[action][option][1].append(expected)
|
||||
## transform dict to tuple
|
||||
#ret = []
|
||||
#for opt_requires in ret_requires.values():
|
||||
# ret_action = []
|
||||
# for require in opt_requires.values():
|
||||
# ret_action.append((require[0], tuple(require[1]), require[2],
|
||||
# require[3], require[4], require[5]))
|
||||
# ret.append(tuple(ret_action))
|
||||
return ret_requires
|
||||
# transform dict to tuple
|
||||
ret = []
|
||||
for opt_requires in ret_requires.values():
|
||||
ret_action = []
|
||||
for require in opt_requires.values():
|
||||
ret_action.append((require[0], tuple(require[1]), require[2],
|
||||
require[3], require[4], require[5]))
|
||||
ret.append(tuple(ret_action))
|
||||
return frozenset(config_action.keys()), tuple(ret)
|
||||
|
||||
|
||||
def validate_callback(callback, callback_params, type_):
|
||||
|
|
|
@ -385,7 +385,7 @@ class Settings(object):
|
|||
if is_cached:
|
||||
return props
|
||||
#FIXME
|
||||
props = self._p_.getproperties(path, opt.impl_getproperties())
|
||||
props = self._p_.getproperties(path, opt._properties)
|
||||
if is_apply_req:
|
||||
props |= self.apply_requires(opt, path)
|
||||
if 'cache' in self:
|
||||
|
@ -589,44 +589,45 @@ class Settings(object):
|
|||
:param path: the option's path in the config
|
||||
:type path: str
|
||||
"""
|
||||
if opt.impl_getrequires() is None:
|
||||
if opt._requires is None:
|
||||
return frozenset()
|
||||
|
||||
# filters the callbacks
|
||||
calc_properties = set()
|
||||
context = self._getcontext()
|
||||
for require in opt.impl_getrequires():
|
||||
expected = tuple(require.get_expected())
|
||||
inverse = require.inverse
|
||||
option = require.option
|
||||
reqpath = self._get_path_by_opt(option)
|
||||
if reqpath == path or reqpath.startswith(path + '.'):
|
||||
raise RequirementError(_("malformed requirements "
|
||||
"imbrication detected for option:"
|
||||
" '{0}' with requirement on: "
|
||||
"'{1}'").format(path, reqpath))
|
||||
try:
|
||||
value = context._getattr(reqpath,
|
||||
force_permissive=True)
|
||||
except PropertiesOptionError as err:
|
||||
if not require.transitive:
|
||||
continue
|
||||
properties = err.proptype
|
||||
if require.same_action and require.action not in properties:
|
||||
raise RequirementError(_("option '{0}' has "
|
||||
"requirement's property "
|
||||
"error: "
|
||||
"{1} {2}").format(opt.impl_getname(),
|
||||
reqpath,
|
||||
properties))
|
||||
# transitive action, force expected
|
||||
value = expected[0]
|
||||
inverse = False
|
||||
if not inverse and value in expected or \
|
||||
inverse and value not in expected:
|
||||
calc_properties.add(require.action)
|
||||
# the calculation cannot be carried out
|
||||
#break
|
||||
for requires in opt._requires:
|
||||
for require in requires:
|
||||
option, expected, action, inverse, \
|
||||
transitive, same_action = require
|
||||
reqpath = self._get_path_by_opt(option)
|
||||
if reqpath == path or reqpath.startswith(path + '.'):
|
||||
raise RequirementError(_("malformed requirements "
|
||||
"imbrication detected for option:"
|
||||
" '{0}' with requirement on: "
|
||||
"'{1}'").format(path, reqpath))
|
||||
try:
|
||||
value = context._getattr(reqpath,
|
||||
force_permissive=True)
|
||||
except PropertiesOptionError as err:
|
||||
if not transitive:
|
||||
continue
|
||||
properties = err.proptype
|
||||
if same_action and action not in properties:
|
||||
raise RequirementError(_("option '{0}' has "
|
||||
"requirement's property "
|
||||
"error: "
|
||||
"{1} {2}").format(opt._name,
|
||||
reqpath,
|
||||
properties))
|
||||
# transitive action, force expected
|
||||
value = expected[0]
|
||||
inverse = False
|
||||
if (not inverse and
|
||||
value in expected or
|
||||
inverse and value not in expected):
|
||||
calc_properties.add(action)
|
||||
# the calculation cannot be carried out
|
||||
break
|
||||
return calc_properties
|
||||
|
||||
def _get_path_by_opt(self, opt):
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
from tiramisu.i18n import _
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
from sqlalchemy.ext.associationproxy import association_proxy
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
|
||||
PickleType, ForeignKey, Table
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||
|
||||
|
||||
#FIXME
|
||||
|
@ -39,68 +41,91 @@ SqlAlchemyBase = declarative_base()
|
|||
|
||||
|
||||
#_Base : object dans la base de donnée
|
||||
# => _RequireOption => il y a une liste d'espect dans _RequireExpected
|
||||
# => _PropertyOption => liste des propriétés
|
||||
# => _Information => dictionnaire avec clef valeur
|
||||
# => _CallbackParam avec des Options
|
||||
require_table = Table('require', SqlAlchemyBase.metadata,
|
||||
Column('left_id', Integer, ForeignKey('requireoption.id')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
def load_requires(collection_type, proxy):
|
||||
def getter(obj):
|
||||
if obj is None:
|
||||
return None
|
||||
ret = []
|
||||
requires = getattr(obj, proxy.value_attr)
|
||||
for require in requires:
|
||||
option = session.query(_Base).filter_by(id=require.option).first()
|
||||
ret.append(tuple([option, require.expected, require.action, require.inverse, require.transitive, require.same_action]))
|
||||
return tuple(ret)
|
||||
|
||||
def setter(obj, value):
|
||||
setattr(obj, proxy.value_attr, value)
|
||||
return getter, setter
|
||||
|
||||
|
||||
class _RequireExpected(SqlAlchemyBase):
|
||||
__tablename__ = 'expected'
|
||||
class _Require(SqlAlchemyBase):
|
||||
__tablename__ = "require"
|
||||
id = Column(Integer, primary_key=True)
|
||||
expected = Column(PickleType)
|
||||
require = Column(Integer, ForeignKey('requireoption.id'))
|
||||
requires_id = Column(Integer, ForeignKey("baseoption.id"), nullable=False)
|
||||
requires = relationship('_RequireOption')
|
||||
|
||||
def __init__(self, expected):
|
||||
self.expected = expected
|
||||
def __init__(self, requires):
|
||||
for require in requires:
|
||||
self.requires.append(_RequireOption(require))
|
||||
|
||||
|
||||
class _RequireOption(SqlAlchemyBase):
|
||||
__tablename__ = 'requireoption'
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = relationship('_Base', lazy='joined', cascade="all, delete-orphan")
|
||||
#option = relationship('_Base')
|
||||
expected = relationship("_RequireExpected")
|
||||
require_id = Column(Integer, ForeignKey("require.id"), nullable=False)
|
||||
option = Column(Integer, nullable=False)
|
||||
_expected = relationship("_RequireExpected", collection_class=list,
|
||||
cascade="all, delete-orphan")
|
||||
expected = association_proxy("_expected", "expected")
|
||||
#expected = Column(String)
|
||||
action = Column(String, nullable=False)
|
||||
inverse = Column(Boolean, default=False)
|
||||
transitive = Column(Boolean, default=True)
|
||||
same_action = Column(Boolean, default=True)
|
||||
|
||||
def __init__(self, option, expected, action, inverse, transitive,
|
||||
same_action):
|
||||
#self.r_opt = option.id
|
||||
self.option = option
|
||||
for expect in expected:
|
||||
self.expected.append(_RequireExpected(expect))
|
||||
def __init__(self, values):
|
||||
option, expected, action, inverse, transitive, same_action = values
|
||||
self.option = option.id
|
||||
self.expected = expected
|
||||
self.action = action
|
||||
self.inverse = inverse
|
||||
self.transitive = transitive
|
||||
self.same_action = same_action
|
||||
|
||||
def get_expected(self):
|
||||
for expected in self.expected:
|
||||
yield(expected.expected)
|
||||
|
||||
#def get_option(self, config):
|
||||
# return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
|
||||
class _RequireExpected(SqlAlchemyBase):
|
||||
__tablename__ = 'expected'
|
||||
id = Column(Integer, primary_key=True)
|
||||
require = Column(Integer, ForeignKey('requireoption.id'), nullable=False)
|
||||
expected = Column(PickleType)
|
||||
|
||||
def __init__(self, expected):
|
||||
#FIXME ne pas creer plusieurs fois la meme _expected_
|
||||
#FIXME pareil avec calc_properties
|
||||
self.expected = expected
|
||||
|
||||
|
||||
class _CalcProperties(SqlAlchemyBase):
|
||||
__tablename__ = 'calcproperty'
|
||||
id = Column(Integer, primary_key=True)
|
||||
require = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
|
||||
name = Column(PickleType)
|
||||
|
||||
def __init__(self, name):
|
||||
#FIXME ne pas creer plusieurs fois la meme _expected_
|
||||
#FIXME pareil avec calc_properties
|
||||
self.name = name
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# properties
|
||||
property_table = Table('property', SqlAlchemyBase.metadata,
|
||||
Column('left_id', Integer, ForeignKey('propertyoption.name')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
|
||||
|
||||
class _PropertyOption(SqlAlchemyBase):
|
||||
__tablename__ = 'propertyoption'
|
||||
name = Column(String, primary_key=True)
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
|
||||
name = Column(String)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
@ -112,7 +137,7 @@ class _PropertyOption(SqlAlchemyBase):
|
|||
class _Information(SqlAlchemyBase):
|
||||
__tablename__ = 'information'
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = Column(Integer, ForeignKey('baseoption.id'))
|
||||
option = Column(Integer, ForeignKey('baseoption.id'), nullable=False)
|
||||
key = Column(String)
|
||||
value = Column(PickleType)
|
||||
|
||||
|
@ -184,11 +209,15 @@ class _Base(SqlAlchemyBase):
|
|||
__tablename__ = 'baseoption'
|
||||
id = Column(Integer, primary_key=True)
|
||||
_name = Column(String)
|
||||
_informations = relationship('_Information')
|
||||
#FIXME not autoload
|
||||
_infos = relationship("_Information",
|
||||
collection_class=attribute_mapped_collection('key'),
|
||||
cascade="all, delete-orphan")
|
||||
_informations = association_proxy("_infos", "value")
|
||||
_default = Column(PickleType)
|
||||
_default_multi = Column(PickleType)
|
||||
_requires = relationship('_RequireOption', secondary=require_table,
|
||||
backref=backref('self_option', enable_typechecks=False))
|
||||
_reqs = relationship("_Require", collection_class=list)
|
||||
_requires = association_proxy("_reqs", "requires", getset_factory=load_requires)
|
||||
_multi = Column(Boolean)
|
||||
_multitype = Column(String)
|
||||
_callback = Column(PickleType)
|
||||
|
@ -197,8 +226,14 @@ class _Base(SqlAlchemyBase):
|
|||
_validator_params = relationship('_CallbackParam')
|
||||
_parent = Column(Integer, ForeignKey('baseoption.id'))
|
||||
_children = relationship('BaseOption', enable_typechecks=False)
|
||||
_properties = relationship('_PropertyOption', secondary=property_table,
|
||||
backref=backref('options', enable_typechecks=False))
|
||||
#FIXME pas 2 fois la meme properties dans la base ...
|
||||
#FIXME not autoload
|
||||
#FIXME normalement tuple ... transforme en set !
|
||||
_props = relationship("_PropertyOption", collection_class=set)
|
||||
_properties = association_proxy("_props", "name")
|
||||
#FIXME fusion avec expected
|
||||
_calc_props = relationship("_CalcProperties", collection_class=set)
|
||||
_calc_properties = association_proxy("_calc_props", "name")
|
||||
_warnings_only = Column(Boolean)
|
||||
_readonly = Column(Boolean, default=False)
|
||||
_consistencies = relationship('_Consistency', secondary=consistency_table,
|
||||
|
@ -206,7 +241,6 @@ class _Base(SqlAlchemyBase):
|
|||
_choice_values = Column(PickleType)
|
||||
_choice_open_values = Column(Boolean)
|
||||
_type = Column(String(50))
|
||||
_r_option = Column(Integer, ForeignKey('requireoption.id'))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'option',
|
||||
'polymorphic_on': _type
|
||||
|
@ -227,8 +261,8 @@ class _Base(SqlAlchemyBase):
|
|||
prop_obj = _PropertyOption(propname)
|
||||
return prop_obj
|
||||
|
||||
def _add_require(self, require):
|
||||
self._requires.append(_RequireOption(*require))
|
||||
#def _add_require(self, require):
|
||||
# self._requires.append(_RequireOption(*require))
|
||||
|
||||
def _add_callback(self, key, values):
|
||||
self._callback_params.append(_CallbackParam(key, values))
|
||||
|
@ -282,7 +316,6 @@ class StorageOptionDescription(object):
|
|||
|
||||
def impl_get_path_by_opt(self, opt):
|
||||
try:
|
||||
print opt, type(opt)
|
||||
return self._cache_paths[1][self._cache_paths[0].index(opt.id)]
|
||||
except ValueError:
|
||||
raise AttributeError(_('no option {0} found').format(opt))
|
||||
|
|
Loading…
Reference in New Issue