sqlalchemy has a storage
This commit is contained in:
parent
068f68460d
commit
661f844ce6
|
@ -481,6 +481,7 @@ def test_callback_master_and_slaves_slave_list():
|
|||
assert cfg.val1.val2 == ['val', 'val']
|
||||
cfg.val1.val1 = ['val1']
|
||||
#wrong len
|
||||
print cfg.val1.val2
|
||||
raises(SlaveError, 'cfg.val1.val2')
|
||||
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ def carry_out_calculation(option, config, callback, callback_params,
|
|||
for callbk in callbacks.params:
|
||||
if callbk.option is not None:
|
||||
# callbk is something link (opt, True|False)
|
||||
opt = callbk.get_option(config)
|
||||
opt = callbk.option
|
||||
force_permissive = callbk.force_permissive
|
||||
path = config.cfgimpl_get_description().impl_get_path_by_opt(
|
||||
opt)
|
||||
|
|
|
@ -28,17 +28,15 @@ from IPy import IP
|
|||
import warnings
|
||||
#from pickle import loads, dumps
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
|
||||
PickleType, ForeignKey, Table
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from tiramisu.error import ConfigError, ConflictError, ValueWarning
|
||||
from tiramisu.setting import groups, multitypes
|
||||
from tiramisu.i18n import _
|
||||
from tiramisu.autolib import carry_out_calculation
|
||||
|
||||
#FIXME : need storage...
|
||||
from tiramisu.storage.sqlalchemy.option import StorageBase, StorageOptionDescription
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
|
||||
name_regexp = re.compile(r'^\d+')
|
||||
forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
|
||||
'make_dict', 'unwrap_from_path', 'read_only',
|
||||
|
@ -58,170 +56,7 @@ def valid_name(name):
|
|||
return False
|
||||
#____________________________________________________________
|
||||
#
|
||||
|
||||
engine = create_engine('sqlite:///:memory:')
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class _RequireExpected(Base):
|
||||
__tablename__ = 'expected'
|
||||
id = Column(Integer, primary_key=True)
|
||||
expected = Column(PickleType)
|
||||
require = Column(Integer, ForeignKey('require.id'))
|
||||
|
||||
def __init__(self, expected):
|
||||
self.expected = expected
|
||||
|
||||
|
||||
class _RequireOption(Base):
|
||||
__tablename__ = 'require'
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = Column(Integer, ForeignKey('baseoption.id'))
|
||||
r_opt = Column(Integer)
|
||||
expected = relationship("_RequireExpected")
|
||||
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
|
||||
for expect in expected:
|
||||
self.expected.append(_RequireExpected(expect))
|
||||
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)
|
||||
|
||||
|
||||
property_table = Table('property', Base.metadata,
|
||||
Column('left_id', Integer, ForeignKey('propertyoption.name')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
|
||||
|
||||
class _PropertyOption(Base):
|
||||
__tablename__ = 'propertyoption'
|
||||
name = Column(String, primary_key=True)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class _Information(Base):
|
||||
__tablename__ = 'information'
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = Column(Integer, ForeignKey('baseoption.id'))
|
||||
key = Column(String)
|
||||
value = Column(PickleType)
|
||||
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
class _CallbackParamOption(Base):
|
||||
__tablename__ = 'callback_param_option'
|
||||
id = Column(Integer, primary_key=True)
|
||||
callback_param = Column(Integer, ForeignKey('callback_param.id'))
|
||||
option = Column(Integer)
|
||||
force_permissive = Column(Boolean)
|
||||
value = Column(PickleType)
|
||||
|
||||
def __init__(self, option=None, force_permissive=None, value=None):
|
||||
if value is not None:
|
||||
self.value = value
|
||||
else:
|
||||
if isinstance(option, SymLinkOption):
|
||||
option = option._opt
|
||||
self.option = option.id
|
||||
self.force_permissive = force_permissive
|
||||
|
||||
def get_option(self, config):
|
||||
return config.cfgimpl_get_description().impl_get_opt_by_id(self.option)
|
||||
|
||||
|
||||
class _CallbackParam(Base):
|
||||
__tablename__ = 'callback_param'
|
||||
id = Column(Integer, primary_key=True)
|
||||
callback = Column(Integer, ForeignKey('baseoption.id'))
|
||||
name = Column(String)
|
||||
params = relationship('_CallbackParamOption')
|
||||
|
||||
def __init__(self, name, params):
|
||||
self.name = name
|
||||
for param in params:
|
||||
if isinstance(param, tuple):
|
||||
self.params.append(_CallbackParamOption(option=param[0],
|
||||
force_permissive=param[1]))
|
||||
else:
|
||||
self.params.append(_CallbackParamOption(value=param))
|
||||
|
||||
|
||||
consistency_table = Table('consistencyopt', Base.metadata,
|
||||
Column('left_id', Integer, ForeignKey('consistency.id')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
|
||||
|
||||
class _Consistency(Base):
|
||||
__tablename__ = 'consistency'
|
||||
id = Column(Integer, primary_key=True)
|
||||
func = Column(PickleType)
|
||||
|
||||
def __init__(self, func, all_cons_opts):
|
||||
self.func = func
|
||||
for option in all_cons_opts:
|
||||
option._consistencies.append(self)
|
||||
|
||||
|
||||
class BaseOption(Base):
|
||||
"""This abstract base class stands for attribute access
|
||||
in options that have to be set only once, it is of course done in the
|
||||
__setattr__ method
|
||||
"""
|
||||
__tablename__ = 'baseoption'
|
||||
id = Column(Integer, primary_key=True)
|
||||
_name = Column(String)
|
||||
_informations = relationship('_Information')
|
||||
_default = Column(PickleType)
|
||||
_default_multi = Column(PickleType)
|
||||
_requires = relationship('_RequireOption')
|
||||
_multi = Column(Boolean)
|
||||
_multitype = Column(String)
|
||||
_callback = Column(PickleType)
|
||||
_callback_params = relationship('_CallbackParam')
|
||||
_validator = Column(PickleType)
|
||||
_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))
|
||||
_warnings_only = Column(Boolean)
|
||||
_readonly = Column(Boolean, default=False)
|
||||
_consistencies = relationship('_Consistency', secondary=consistency_table,
|
||||
backref=backref('options', enable_typechecks=False))
|
||||
_choice_values = Column(PickleType)
|
||||
_choice_open_values = Column(Boolean)
|
||||
_type = Column(String(50))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'person',
|
||||
'polymorphic_on': _type
|
||||
}
|
||||
#FIXME devrait etre une table
|
||||
_optiondescription_group_type = Column(String)
|
||||
#__slots__ = ('_name', '_requires', '_properties', '_readonly',
|
||||
# '_calc_properties', '_impl_informations',
|
||||
# '_state_readonly', '_state_requires', '_stated')
|
||||
|
||||
class Base(StorageBase):
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
|
@ -235,7 +70,7 @@ class BaseOption(Base):
|
|||
if requires is not None:
|
||||
for values in requires.values():
|
||||
for require in values.values():
|
||||
self._requires.append(_RequireOption(*require))
|
||||
self._add_require(require)
|
||||
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))
|
||||
|
@ -271,7 +106,7 @@ class BaseOption(Base):
|
|||
self._validator = validator
|
||||
if validator_params is not None:
|
||||
for key, values in validator_params.items():
|
||||
self._validator_params.append(_CallbackParam(key, values))
|
||||
self._add_validator(key, values)
|
||||
if callback is None and callback_params is not None:
|
||||
raise ValueError(_("params defined for a callback function but "
|
||||
"no callback defined"
|
||||
|
@ -281,7 +116,7 @@ class BaseOption(Base):
|
|||
self._callback = callback
|
||||
if callback_params is not None:
|
||||
for key, values in callback_params.items():
|
||||
self._callback_params.append(_CallbackParam(key, values))
|
||||
self._add_callback(key, values)
|
||||
if requires is not None and properties is not tuple():
|
||||
set_forbidden_properties = set(properties) & set(requires.keys())
|
||||
if set_forbidden_properties != frozenset():
|
||||
|
@ -298,41 +133,19 @@ class BaseOption(Base):
|
|||
else:
|
||||
self._default = default
|
||||
for prop in properties:
|
||||
prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == prop).first()
|
||||
if prop_obj is None:
|
||||
prop_obj = _PropertyOption(prop)
|
||||
self._properties.append(prop_obj)
|
||||
self._properties.append(self._get_property_object(prop))
|
||||
self._warnings_only = warnings_only
|
||||
return super(Base, self).__init__()
|
||||
|
||||
# ____________________________________________________________
|
||||
# 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")
|
||||
"""
|
||||
#FIXME pas append ! remplacer !
|
||||
info = session.query(_Information).filter_by(option=self.id, key=key).first()
|
||||
if info is None:
|
||||
self._informations.append(_Information(key, value))
|
||||
else:
|
||||
info.value = value
|
||||
|
||||
def impl_get_information(self, key, default=None):
|
||||
"""retrieves one information's item
|
||||
|
||||
:param key: the item string (ex: "help")
|
||||
"""
|
||||
info = session.query(_Information).filter_by(option=self.id, key=key).first()
|
||||
if info is not None:
|
||||
return info.value
|
||||
elif default is not None:
|
||||
return default
|
||||
else:
|
||||
raise ValueError(_("information's item not found: {0}").format(
|
||||
key))
|
||||
class BaseOption(Base):
|
||||
"""This abstract base class stands for attribute access
|
||||
in options that have to be set only once, it is of course done in the
|
||||
__setattr__ method
|
||||
"""
|
||||
#__slots__ = ('_name', '_requires', '_properties', '_readonly',
|
||||
# '_calc_properties', '_impl_informations',
|
||||
# '_state_readonly', '_state_requires', '_stated')
|
||||
|
||||
# ____________________________________________________________
|
||||
# serialize object
|
||||
|
@ -500,8 +313,6 @@ class Option(BaseOption):
|
|||
validator_params, properties,
|
||||
warnings_only, choice_values,
|
||||
choice_open_values)
|
||||
session.add(self)
|
||||
session.commit()
|
||||
|
||||
#def __setattr__(self, name, value):
|
||||
# """set once and only once some attributes in the option,
|
||||
|
@ -749,7 +560,7 @@ class Option(BaseOption):
|
|||
else:
|
||||
self._launch_consistency(func, self, value, None,
|
||||
None, all_cons_opts)
|
||||
_Consistency(func, all_cons_opts)
|
||||
self._add_consistency(func, all_cons_opts)
|
||||
self.impl_validate(self.impl_getdefault())
|
||||
|
||||
def _cons_not_equal(self, opts, vals):
|
||||
|
@ -856,10 +667,6 @@ class ChoiceOption(Option):
|
|||
"""
|
||||
|
||||
#__slots__ = ('_values', '_open_values')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'choice',
|
||||
}
|
||||
|
||||
def __init__(self, name, doc, values, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, open_values=False, validator=None,
|
||||
|
@ -901,9 +708,6 @@ class ChoiceOption(Option):
|
|||
class BoolOption(Option):
|
||||
"represents a choice between ``True`` and ``False``"
|
||||
# __slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'bool',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
if not isinstance(value, bool):
|
||||
|
@ -913,9 +717,6 @@ class BoolOption(Option):
|
|||
class IntOption(Option):
|
||||
"represents a choice of an integer"
|
||||
# __slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'int',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
if not isinstance(value, int):
|
||||
|
@ -925,9 +726,6 @@ class IntOption(Option):
|
|||
class FloatOption(Option):
|
||||
"represents a choice of a floating point number"
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'float',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
if not isinstance(value, float):
|
||||
|
@ -937,9 +735,6 @@ class FloatOption(Option):
|
|||
class StrOption(Option):
|
||||
"represents the choice of a string"
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'string',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
if not isinstance(value, str):
|
||||
|
@ -955,9 +750,6 @@ else:
|
|||
class UnicodeOption(Option):
|
||||
"represents the choice of a unicode string"
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'unicode',
|
||||
}
|
||||
_empty = u''
|
||||
|
||||
def _validate(self, value):
|
||||
|
@ -967,9 +759,6 @@ else:
|
|||
|
||||
class SymLinkOption(BaseOption):
|
||||
#__slots__ = ('_name', '_opt', '_state_opt', '_readonly', '_parent')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'symlink',
|
||||
}
|
||||
#not return _opt consistencies
|
||||
#_consistencies = None
|
||||
|
||||
|
@ -982,8 +771,7 @@ class SymLinkOption(BaseOption):
|
|||
self._opt = opt
|
||||
self._readonly = True
|
||||
self._parent = None
|
||||
session.add(self)
|
||||
session.commit()
|
||||
self.commit()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in ('_opt', '_opt_type', '_readonly', 'impl_getname'):
|
||||
|
@ -1001,17 +789,13 @@ class SymLinkOption(BaseOption):
|
|||
super(SymLinkOption, self)._impl_setstate(descr)
|
||||
|
||||
def impl_get_information(self, key, default=None):
|
||||
#FIXME ne devrait pas etre util si ?
|
||||
#FIXME ne devrait pas etre utile si ?
|
||||
return self._opt.impl_get_information(key, default)
|
||||
|
||||
|
||||
class IPOption(Option):
|
||||
"represents the choice of an ip"
|
||||
#__slots__ = ('_private_only', '_allow_reserved')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'ip',
|
||||
}
|
||||
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
|
@ -1061,10 +845,6 @@ class PortOption(Option):
|
|||
see: http://en.wikipedia.org/wiki/Port_numbers
|
||||
"""
|
||||
#__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'port',
|
||||
}
|
||||
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
|
@ -1126,10 +906,6 @@ class PortOption(Option):
|
|||
class NetworkOption(Option):
|
||||
"represents the choice of a network"
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'network',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
try:
|
||||
IP(value)
|
||||
|
@ -1145,9 +921,6 @@ class NetworkOption(Option):
|
|||
class NetmaskOption(Option):
|
||||
"represents the choice of a netmask"
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'netmask',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
try:
|
||||
|
@ -1201,9 +974,6 @@ class NetmaskOption(Option):
|
|||
|
||||
class BroadcastOption(Option):
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'broadcast',
|
||||
}
|
||||
|
||||
def _validate(self, value):
|
||||
try:
|
||||
|
@ -1232,9 +1002,6 @@ class DomainnameOption(Option):
|
|||
fqdn: with tld, not supported yet
|
||||
"""
|
||||
#__slots__ = ('_dom_type', '_allow_ip', '_allow_without_dot', '_domain_re')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'domainname',
|
||||
}
|
||||
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
|
@ -1297,9 +1064,6 @@ class DomainnameOption(Option):
|
|||
|
||||
class EmailOption(DomainnameOption):
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'email',
|
||||
}
|
||||
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
|
||||
|
||||
def _validate(self, value):
|
||||
|
@ -1316,9 +1080,6 @@ class EmailOption(DomainnameOption):
|
|||
|
||||
class URLOption(DomainnameOption):
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'url',
|
||||
}
|
||||
proto_re = re.compile(r'(http|https)://')
|
||||
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||
|
||||
|
@ -1355,9 +1116,6 @@ class URLOption(DomainnameOption):
|
|||
|
||||
class FilenameOption(Option):
|
||||
#__slots__ = tuple()
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'file',
|
||||
}
|
||||
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
|
||||
|
||||
def _validate(self, value):
|
||||
|
@ -1366,7 +1124,7 @@ class FilenameOption(Option):
|
|||
raise ValueError(_('invalid filename'))
|
||||
|
||||
|
||||
class OptionDescription(BaseOption):
|
||||
class OptionDescription(BaseOption, StorageOptionDescription):
|
||||
"""Config's schema (organisation, group) and container of Options
|
||||
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
|
||||
"""
|
||||
|
@ -1375,9 +1133,6 @@ class OptionDescription(BaseOption):
|
|||
# '_cache_consistencies', '_calc_properties', '__weakref__',
|
||||
# '_readonly', '_impl_informations', '_state_requires',
|
||||
# '_stated', '_state_readonly')
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'optiondescription',
|
||||
}
|
||||
|
||||
def __init__(self, name, doc, children, requires=None, properties=None):
|
||||
"""
|
||||
|
@ -1385,8 +1140,6 @@ class OptionDescription(BaseOption):
|
|||
|
||||
"""
|
||||
super(OptionDescription, self).__init__(name, doc=doc, requires=requires, properties=properties)
|
||||
session.add(self)
|
||||
session.commit()
|
||||
child_names = [child.impl_getname() for child in children]
|
||||
#better performance like this
|
||||
valid_child = copy(child_names)
|
||||
|
@ -1528,28 +1281,6 @@ class OptionDescription(BaseOption):
|
|||
self._cache_consistencies[opt] = tuple(cons)
|
||||
self._readonly = True
|
||||
|
||||
def impl_get_opt_by_path(self, path):
|
||||
try:
|
||||
#FIXME
|
||||
idx = self._cache_paths[1].index(path)
|
||||
opt_id = self._cache_paths[0][idx]
|
||||
return session.query(BaseOption).filter_by(id=opt_id).first()
|
||||
except ValueError:
|
||||
raise AttributeError(_('no option for path {0}').format(path))
|
||||
|
||||
def impl_get_opt_by_id(self, opt_id):
|
||||
try:
|
||||
#FIXME
|
||||
#idx = self._cache_paths[0].index(opt_id)
|
||||
return session.query(BaseOption).filter_by(id=opt_id).first()
|
||||
except ValueError:
|
||||
raise AttributeError(_('no id {0} found').format(opt_id))
|
||||
|
||||
def impl_get_path_by_opt(self, opt):
|
||||
try:
|
||||
return self._cache_paths[1][self._cache_paths[0].index(opt.id)]
|
||||
except ValueError:
|
||||
raise AttributeError(_('no option {0} found').format(opt))
|
||||
|
||||
# ____________________________________________________________
|
||||
def impl_set_group_type(self, group_type):
|
||||
|
@ -1800,8 +1531,3 @@ def validate_callback(callback, callback_params, type_):
|
|||
' not a {0} for second argument'
|
||||
).format(type_, type(
|
||||
force_permissive)))
|
||||
|
||||
#FIXME
|
||||
Base.metadata.create_all(engine)
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
|
|
@ -588,7 +588,7 @@ class Settings(object):
|
|||
for require in opt.impl_getrequires():
|
||||
expected = tuple(require.get_expected())
|
||||
inverse = require.inverse
|
||||
option = require.get_option(self.context())
|
||||
option = require.option
|
||||
reqpath = self._get_path_by_opt(option)
|
||||
if reqpath == path or reqpath.startswith(path + '.'):
|
||||
raise RequirementError(_("malformed requirements "
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
""
|
||||
# Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# ____________________________________________________________
|
||||
from tiramisu.setting import multitypes
|
||||
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
|
||||
PickleType, ForeignKey, Table
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
|
||||
#FIXME
|
||||
engine = create_engine('sqlite:///:memory:')
|
||||
SqlAlchemyBase = declarative_base()
|
||||
#____________________________________________________________
|
||||
#
|
||||
# require
|
||||
require_table = Table('require', SqlAlchemyBase.metadata,
|
||||
Column('left_id', Integer, ForeignKey('requireoption.id')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
|
||||
class _RequireExpected(SqlAlchemyBase):
|
||||
__tablename__ = 'expected'
|
||||
id = Column(Integer, primary_key=True)
|
||||
expected = Column(PickleType)
|
||||
require = Column(Integer, ForeignKey('requireoption.id'))
|
||||
|
||||
def __init__(self, expected):
|
||||
self.expected = expected
|
||||
|
||||
|
||||
class _RequireOption(SqlAlchemyBase):
|
||||
__tablename__ = 'requireoption'
|
||||
id = Column(Integer, primary_key=True)
|
||||
r_opt = Column(Integer)
|
||||
expected = relationship("_RequireExpected")
|
||||
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
|
||||
for expect in expected:
|
||||
self.expected.append(_RequireExpected(expect))
|
||||
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)
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# 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)
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# information
|
||||
class _Information(SqlAlchemyBase):
|
||||
__tablename__ = 'information'
|
||||
id = Column(Integer, primary_key=True)
|
||||
option = Column(Integer, ForeignKey('baseoption.id'))
|
||||
key = Column(String)
|
||||
value = Column(PickleType)
|
||||
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# callback
|
||||
class _CallbackParamOption(SqlAlchemyBase):
|
||||
__tablename__ = 'callback_param_option'
|
||||
id = Column(Integer, primary_key=True)
|
||||
callback_param = Column(Integer, ForeignKey('callback_param.id'))
|
||||
option = Column(Integer)
|
||||
force_permissive = Column(Boolean)
|
||||
value = Column(PickleType)
|
||||
|
||||
def __init__(self, option=None, force_permissive=None, value=None):
|
||||
if value is not None:
|
||||
self.value = value
|
||||
else:
|
||||
self.option = option.id
|
||||
self.force_permissive = force_permissive
|
||||
|
||||
|
||||
class _CallbackParam(SqlAlchemyBase):
|
||||
__tablename__ = 'callback_param'
|
||||
id = Column(Integer, primary_key=True)
|
||||
callback = Column(Integer, ForeignKey('baseoption.id'))
|
||||
name = Column(String)
|
||||
params = relationship('_CallbackParamOption')
|
||||
|
||||
def __init__(self, name, params):
|
||||
self.name = name
|
||||
for param in params:
|
||||
if isinstance(param, tuple):
|
||||
self.params.append(_CallbackParamOption(option=param[0],
|
||||
force_permissive=param[1]))
|
||||
else:
|
||||
self.params.append(_CallbackParamOption(value=param))
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# consistency
|
||||
consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata,
|
||||
Column('left_id', Integer, ForeignKey('consistency.id')),
|
||||
Column('right_id', Integer, ForeignKey('baseoption.id'))
|
||||
)
|
||||
|
||||
|
||||
class _Consistency(SqlAlchemyBase):
|
||||
__tablename__ = 'consistency'
|
||||
id = Column(Integer, primary_key=True)
|
||||
func = Column(PickleType)
|
||||
|
||||
def __init__(self, func, all_cons_opts):
|
||||
self.func = func
|
||||
for option in all_cons_opts:
|
||||
option._consistencies.append(self)
|
||||
|
||||
|
||||
#____________________________________________________________
|
||||
#
|
||||
# Base
|
||||
class _Base(SqlAlchemyBase):
|
||||
__tablename__ = 'baseoption'
|
||||
id = Column(Integer, primary_key=True)
|
||||
_name = Column(String)
|
||||
_informations = relationship('_Information')
|
||||
_default = Column(PickleType)
|
||||
_default_multi = Column(PickleType)
|
||||
_requires = relationship('_RequireOption', secondary=require_table,
|
||||
backref=backref('option', enable_typechecks=False))
|
||||
_multi = Column(Boolean)
|
||||
_multitype = Column(String)
|
||||
_callback = Column(PickleType)
|
||||
_callback_params = relationship('_CallbackParam')
|
||||
_validator = Column(PickleType)
|
||||
_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))
|
||||
_warnings_only = Column(Boolean)
|
||||
_readonly = Column(Boolean, default=False)
|
||||
_consistencies = relationship('_Consistency', secondary=consistency_table,
|
||||
backref=backref('options', enable_typechecks=False))
|
||||
_choice_values = Column(PickleType)
|
||||
_choice_open_values = Column(Boolean)
|
||||
_type = Column(String(50))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'option',
|
||||
'polymorphic_on': _type
|
||||
}
|
||||
#FIXME devrait etre une table
|
||||
_optiondescription_group_type = Column(String)
|
||||
|
||||
def __init__(self):
|
||||
self.commit()
|
||||
|
||||
def commit(self):
|
||||
session.add(self)
|
||||
session.commit()
|
||||
|
||||
def _get_property_object(self, propname):
|
||||
prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == propname).first()
|
||||
if prop_obj is None:
|
||||
prop_obj = _PropertyOption(propname)
|
||||
return prop_obj
|
||||
|
||||
def _add_require(self, require):
|
||||
self._requires.append(_RequireOption(*require))
|
||||
|
||||
def _add_callback(self, key, values):
|
||||
self._callback_params.append(_CallbackParam(key, values))
|
||||
|
||||
def _add_validator(self, key, values):
|
||||
self._validator_params.append(_CallbackParam(key, values))
|
||||
|
||||
def _add_consistency(self, func, all_cons_opts):
|
||||
_Consistency(func, all_cons_opts)
|
||||
# ____________________________________________________________
|
||||
# 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")
|
||||
"""
|
||||
info = session.query(_Information).filter_by(option=self.id, key=key).first()
|
||||
#FIXME pas append ! remplacer !
|
||||
if info is None:
|
||||
self._informations.append(_Information(key, value))
|
||||
else:
|
||||
info.value = value
|
||||
|
||||
def impl_get_information(self, key, default=None):
|
||||
"""retrieves one information's item
|
||||
|
||||
:param key: the item string (ex: "help")
|
||||
"""
|
||||
info = session.query(_Information).filter_by(option=self.id, key=key).first()
|
||||
if info is not None:
|
||||
return info.value
|
||||
elif default is not None:
|
||||
return default
|
||||
else:
|
||||
raise ValueError(_("information's item not found: {0}").format(
|
||||
key))
|
||||
|
||||
|
||||
class StorageOptionDescription(object):
|
||||
def impl_get_opt_by_path(self, path):
|
||||
try:
|
||||
#FIXME
|
||||
idx = self._cache_paths[1].index(path)
|
||||
opt_id = self._cache_paths[0][idx]
|
||||
return session.query(_Base).filter_by(id=opt_id).first()
|
||||
except ValueError:
|
||||
raise AttributeError(_('no option for path {0}').format(path))
|
||||
|
||||
def impl_get_path_by_opt(self, opt):
|
||||
try:
|
||||
return self._cache_paths[1][self._cache_paths[0].index(opt)]
|
||||
except ValueError:
|
||||
raise AttributeError(_('no option {0} found').format(opt))
|
||||
|
||||
|
||||
class StorageBase(_Base):
|
||||
@declared_attr
|
||||
def __mapper_args__(self):
|
||||
return {'polymorphic_identity': self.__name__.lower()}
|
||||
|
||||
|
||||
#FIXME
|
||||
SqlAlchemyBase.metadata.create_all(engine)
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"default plugin for cache: set it in a simple dictionary"
|
||||
"utils used by storage"
|
||||
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
|
Loading…
Reference in New Issue