storage no more in setting.py, code is now in storage/__init__.py

This commit is contained in:
Emmanuel Garette 2013-09-06 23:15:28 +02:00
parent 18fc5db4ac
commit 22bfbb9fa4
17 changed files with 437 additions and 196 deletions

121
test/test_state.py Normal file
View file

@ -0,0 +1,121 @@
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
OptionDescription
from pickle import dumps, loads
def _get_slots(opt):
slots = set()
for subclass in opt.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
return slots
def _no_state(opt):
for attr in _get_slots(opt):
if 'state' in attr:
try:
getattr(opt, attr)
except:
pass
else:
raise Exception('opt should have already attribute {0}'.format(attr))
def _diff_opt(opt1, opt2):
attr1 = set(_get_slots(opt1))
attr2 = set(_get_slots(opt2))
diff1 = attr1 - attr2
diff2 = attr2 - attr1
if diff1 != set():
raise Exception('more attribute in opt1 {0}'.format(list(diff1)))
if diff2 != set():
raise Exception('more attribute in opt2 {0}'.format(list(diff2)))
for attr in attr1:
if attr in ['_cache_paths']:
continue
err1 = False
err2 = False
val1 = None
val2 = None
try:
val1 = getattr(opt1, attr)
except:
err1 = True
try:
val2 = getattr(opt2, attr)
except:
err2 = True
assert err1 == err2
if val1 is None:
assert val1 == val2
elif attr == '_children':
assert val1[0] == val2[0]
for index, _opt in enumerate(val1[1]):
assert _opt._name == val2[1][index]._name
elif attr == '_requires':
assert val1[0][0][0]._name == val2[0][0][0]._name
assert val1[0][0][1:] == val2[0][0][1:]
elif attr == '_opt':
assert val1._name == val2._name
elif attr == '_consistencies':
# dict is only a cache
if isinstance(val1, list):
for index, consistency in enumerate(val1):
assert consistency[0] == val2[index][0]
assert consistency[1]._name == val2[index][1]._name
else:
assert val1 == val2
def test_diff_opt():
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
#u.impl_add_consistency('not_equal', b)
s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o])
a = dumps(o1)
q = loads(a)
_diff_opt(o1, q)
_diff_opt(o1.o, q.o)
_diff_opt(o1.o.b, q.o.b)
_diff_opt(o1.o.u, q.o.u)
_diff_opt(o1.o.s, q.o.s)
def test_diff_opt_cache():
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
u.impl_add_consistency('not_equal', b)
s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o])
o1.impl_build_cache()
a = dumps(o1)
q = loads(a)
_diff_opt(o1, q)
_diff_opt(o1.o, q.o)
_diff_opt(o1.o.b, q.o.b)
_diff_opt(o1.o.u, q.o.u)
_diff_opt(o1.o.s, q.o.s)
def test_no_state_attr():
# all _state_xxx attributes should be deleted
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o])
a = dumps(o1)
q = loads(a)
_no_state(q)
_no_state(q.o)
_no_state(q.o.b)
_no_state(q.o.u)
_no_state(q.o.s)

View file

@ -5,7 +5,7 @@ import autopath
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import BoolOption, OptionDescription from tiramisu.option import BoolOption, OptionDescription
from tiramisu.setting import owners from tiramisu.setting import owners
from tiramisu.setting import list_sessions, delete_session from tiramisu.storage import list_sessions, delete_session
def test_non_persistent(): def test_non_persistent():

View file

@ -23,7 +23,8 @@
import weakref import weakref
from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.option import OptionDescription, Option, SymLinkOption
from tiramisu.setting import groups, Settings, default_encoding, get_storage from tiramisu.setting import groups, Settings, default_encoding
from tiramisu.storage import get_storage
from tiramisu.value import Values from tiramisu.value import Values
from tiramisu.i18n import _ from tiramisu.i18n import _
@ -535,9 +536,9 @@ class Config(CommonConfig):
:param persistent: if persistent, don't delete storage when leaving :param persistent: if persistent, don't delete storage when leaving
:type persistent: `boolean` :type persistent: `boolean`
""" """
storage = get_storage(self, session_id, persistent) settings, values = get_storage(self, session_id, persistent)
self._impl_settings = Settings(self, storage) self._impl_settings = Settings(self, settings)
self._impl_values = Values(self, storage) self._impl_values = Values(self, values)
super(Config, self).__init__(descr, weakref.ref(self)) super(Config, self).__init__(descr, weakref.ref(self))
self._impl_build_all_paths() self._impl_build_all_paths()
self._impl_meta = None self._impl_meta = None
@ -575,9 +576,9 @@ class Config(CommonConfig):
# child._impl_meta = self # child._impl_meta = self
# self._impl_children = children # self._impl_children = children
# storage = get_storage(self, session_id, persistent) # settings, values = get_storage(self, session_id, persistent)
# self._impl_settings = Settings(self, storage) # self._impl_settings = Settings(self, settings)
# self._impl_values = Values(self, storage) # self._impl_values = Values(self, values)
# self._impl_meta = None # self._impl_meta = None
# def cfgimpl_get_children(self): # def cfgimpl_get_children(self):

View file

@ -658,7 +658,7 @@ else:
class SymLinkOption(BaseOption): class SymLinkOption(BaseOption):
__slots__ = ('_name', '_opt', '_state_opt', '_consistencies') __slots__ = ('_name', '_opt', '_state_opt')
_opt_type = 'symlink' _opt_type = 'symlink'
#not return _opt consistencies #not return _opt consistencies
_consistencies = {} _consistencies = {}

View file

@ -24,7 +24,7 @@ from time import time
from copy import copy from copy import copy
import weakref import weakref
from tiramisu.error import (RequirementError, PropertiesOptionError, from tiramisu.error import (RequirementError, PropertiesOptionError,
ConstError, ConfigError) ConstError)
from tiramisu.i18n import _ from tiramisu.i18n import _
@ -38,26 +38,6 @@ rw_append = set(['frozen', 'disabled', 'validator', 'hidden'])
default_properties = ('expire', 'validator') default_properties = ('expire', 'validator')
class StorageType:
default_storage = 'dictionary'
storage_type = None
def set_storage(self, name):
if self.storage_type is not None:
raise ConfigError(_('storage_type is already set, cannot rebind it'))
self.storage_type = name
def get_storage(self):
if self.storage_type is None:
self.storage_type = self.default_storage
storage = self.storage_type
return 'tiramisu.storage.{0}.storage'.format(
storage)
storage_type = StorageType()
class _NameSpace: class _NameSpace:
"""convenient class that emulates a module """convenient class that emulates a module
and builds constants (that is, unique names)""" and builds constants (that is, unique names)"""
@ -203,39 +183,6 @@ class Property(object):
return str(list(self._properties)) return str(list(self._properties))
def set_storage(name, **args):
storage_type.set_storage(name)
settings = __import__(storage_type.get_storage(), globals(), locals(),
['Setting']).Setting()
for option, value in args.items():
try:
getattr(settings, option)
setattr(settings, option, value)
except AttributeError:
raise ValueError(_('option {0} not already exists in storage {1}'
'').format(option, name))
def get_storage(context, session_id, persistent):
def gen_id(config):
return str(id(config)) + str(time())
if session_id is None:
session_id = gen_id(context)
return __import__(storage_type.get_storage(), globals(), locals(),
['Storage']).Storage(session_id, persistent)
def list_sessions():
return __import__(storage_type.get_storage(), globals(), locals(),
['list_sessions']).list_sessions()
def delete_session(session_id):
return __import__(storage_type.get_storage(), globals(), locals(),
['delete_session']).delete_session(session_id)
#____________________________________________________________ #____________________________________________________________
class Settings(object): class Settings(object):
"``Config()``'s configuration options" "``Config()``'s configuration options"
@ -254,9 +201,7 @@ class Settings(object):
# generic owner # generic owner
self._owner = owners.user self._owner = owners.user
self.context = weakref.ref(context) self.context = weakref.ref(context)
import_lib = 'tiramisu.storage.{0}.setting'.format(storage.storage) self._p_ = storage
self._p_ = __import__(import_lib, globals(), locals(), ['Settings']
).Settings(storage)
#____________________________________________________________ #____________________________________________________________
# properties methods # properties methods

View file

@ -0,0 +1,90 @@
# Copyright (C) 2013 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
#
# The original `Config` design model is unproudly borrowed from
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
"""Storage connections, executions and managements.
Storage is basic components used to set informations in DB
"""
from time import time
from tiramisu.error import ConfigError
from tiramisu.i18n import _
class StorageType(object):
default_storage = 'dictionary'
storage_type = None
mod = None
def set(self, name):
if self.storage_type is not None:
raise ConfigError(_('storage_type is already set, cannot rebind it'))
self.storage_type = name
def get(self):
if self.storage_type is None:
self.storage_type = self.default_storage
storage = self.storage_type
if self.mod is None:
modulepath = 'tiramisu.storage.{0}'.format(storage)
mod = __import__(modulepath)
for token in modulepath.split(".")[1:]:
mod = getattr(mod, token)
self.mod = mod
return self.mod
storage_type = StorageType()
def set_storage(name, **args):
storage_type.set(name)
settings = storage_type.get().Setting()
for option, value in args.items():
try:
getattr(settings, option)
setattr(settings, option, value)
except AttributeError:
raise ValueError(_('option {0} not already exists in storage {1}'
'').format(option, name))
def get_storage(context, session_id, persistent):
def gen_id(config):
return str(id(config)) + str(time())
if session_id is None:
session_id = gen_id(context)
imp = storage_type.get()
storage = imp.Storage(session_id, persistent)
return imp.Settings(storage), imp.Values(storage)
def list_sessions():
return storage_type.get().list_sessions()
def delete_session(session_id):
return storage_type.get().delete_session(session_id)
#__all__ = (,)

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"default plugin for storage: set it in a simple dictionary"
# Copyright (C) 2013 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 .value import Values
from .setting import Settings
from .storage import Storage, list_sessions, delete_session
__all__ = (Values, Settings, Storage, list_sessions, delete_session)

View file

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
"default plugin for cache: set it in a simple dictionary"
# Copyright (C) 2013 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
#
# ____________________________________________________________
class Cache(object):
__slots__ = ('_cache', 'storage')
key_is_path = False
def __init__(self, storage):
self._cache = {}
self.storage = storage
def setcache(self, cache_type, path, val, time):
self._cache[path] = (val, time)
def getcache(self, cache_type, path, exp):
value, created = self._cache[path]
if exp < created:
return True, value
return False, None
def hascache(self, cache_type, path):
""" path is in the cache
:param cache_type: value | property
:param path: the path's option
"""
return path in self._cache
def reset_expired_cache(self, cache_type, exp):
for key in tuple(self._cache.keys()):
val, created = self._cache[key]
if exp > created:
del(self._cache[key])
def reset_all_cache(self, cache_type):
"empty the cache"
self._cache.clear()
def get_cached(self, cache_type, context):
"""return all values in a dictionary
example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
"""
return self._cache

View file

@ -17,7 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.storage.dictionary.storage import Cache from .cache import Cache
class Settings(Cache): class Settings(Cache):

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"default plugin for cache: set it in a simple dictionary" "default plugin for storage: set it in a simple dictionary"
# Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -38,7 +38,7 @@ def delete_session(session_id):
class Storage(object): class Storage(object):
__slots__ = ('session_id',) __slots__ = ('session_id', 'values', 'settings')
storage = 'dictionary' storage = 'dictionary'
def __init__(self, session_id, persistent): def __init__(self, session_id, persistent):
@ -54,45 +54,3 @@ class Storage(object):
_list_sessions.remove(self.session_id) _list_sessions.remove(self.session_id)
except AttributeError: except AttributeError:
pass pass
class Cache(object):
__slots__ = ('_cache', 'storage')
key_is_path = False
def __init__(self, storage):
self._cache = {}
self.storage = storage
def setcache(self, cache_type, path, val, time):
self._cache[path] = (val, time)
def getcache(self, cache_type, path, exp):
value, created = self._cache[path]
if exp < created:
return True, value
return False, None
def hascache(self, cache_type, path):
""" path is in the cache
:param cache_type: value | property
:param path: the path's option
"""
return path in self._cache
def reset_expired_cache(self, cache_type, exp):
for key in tuple(self._cache.keys()):
val, created = self._cache[key]
if exp > created:
del(self._cache[key])
def reset_all_cache(self, cache_type):
"empty the cache"
self._cache.clear()
def get_cached(self, cache_type, context):
"""return all values in a dictionary
example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
"""
return self._cache

View file

@ -18,7 +18,7 @@
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.storage.dictionary.storage import Cache from .cache import Cache
class Values(Cache): class Values(Cache):

View file

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"set storage in sqlite3"
# Copyright (C) 2013 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 .value import Values
from .setting import Settings
from .storage import Storage, list_sessions, delete_session
__all__ = (Values, Settings, Storage, list_sessions, delete_session)

View file

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
"sqlite3 cache"
# Copyright (C) 2013 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 pickle import dumps, loads
class Cache(object):
__slots__ = ('storage',)
key_is_path = True
def __init__(self, cache_type, storage):
self.storage = storage
cache_table = 'CREATE TABLE IF NOT EXISTS cache_{0}(path '.format(
cache_type)
cache_table += 'text primary key, value text, time real)'
self.storage.execute(cache_table)
# value
def _sqlite_decode_path(self, path):
if path == '_none':
return None
else:
return path
def _sqlite_encode_path(self, path):
if path is None:
return '_none'
else:
return path
def _sqlite_decode(self, value):
return loads(value)
def _sqlite_encode(self, value):
if isinstance(value, list):
value = list(value)
return dumps(value)
def setcache(self, cache_type, path, val, time):
convert_value = self._sqlite_encode(val)
path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM cache_{0} WHERE path = ?".format(
cache_type), (path,), False)
self.storage.execute("INSERT INTO cache_{0}(path, value, time) "
"VALUES (?, ?, ?)".format(cache_type),
(path, convert_value, time))
def getcache(self, cache_type, path, exp):
path = self._sqlite_encode_path(path)
cached = self.storage.select("SELECT value FROM cache_{0} WHERE "
"path = ? AND time >= ?".format(
cache_type), (path, exp))
if cached is None:
return False, None
else:
return True, self._sqlite_decode(cached[0])
def hascache(self, cache_type, path):
path = self._sqlite_encode_path(path)
return self.storage.select("SELECT value FROM cache_{0} WHERE "
"path = ?".format(cache_type),
(path,)) is not None
def reset_expired_cache(self, cache_type, exp):
self.storage.execute("DELETE FROM cache_{0} WHERE time < ?".format(
cache_type), (exp,))
def reset_all_cache(self, cache_type):
self.storage.execute("DELETE FROM cache_{0}".format(cache_type))
def get_cached(self, cache_type, context):
"""return all values in a dictionary
example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
"""
ret = {}
for path, value, time in self.storage.select("SELECT * FROM cache_{0}"
"".format(cache_type),
only_one=False):
path = self._sqlite_decode_path(path)
value = self._sqlite_decode(value)
ret[path] = (value, time)
return ret

View file

@ -17,7 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.storage.sqlite3.storage import Cache from .cache import Cache
class Settings(Cache): class Settings(Cache):

View file

@ -18,7 +18,6 @@
# #
# ____________________________________________________________ # ____________________________________________________________
from pickle import dumps, loads
from os import unlink from os import unlink
from os.path import basename, splitext, join from os.path import basename, splitext, join
import sqlite3 import sqlite3
@ -79,81 +78,3 @@ class Storage(object):
self._conn.close() self._conn.close()
if not self.persistent: if not self.persistent:
delete_session(self._session_id) delete_session(self._session_id)
class Cache(object):
__slots__ = ('storage',)
key_is_path = True
def __init__(self, cache_type, storage):
self.storage = storage
cache_table = 'CREATE TABLE IF NOT EXISTS cache_{0}(path '.format(
cache_type)
cache_table += 'text primary key, value text, time real)'
self.storage.execute(cache_table)
# value
def _sqlite_decode_path(self, path):
if path == '_none':
return None
else:
return path
def _sqlite_encode_path(self, path):
if path is None:
return '_none'
else:
return path
def _sqlite_decode(self, value):
return loads(value)
def _sqlite_encode(self, value):
if isinstance(value, list):
value = list(value)
return dumps(value)
def setcache(self, cache_type, path, val, time):
convert_value = self._sqlite_encode(val)
path = self._sqlite_encode_path(path)
self.storage.execute("DELETE FROM cache_{0} WHERE path = ?".format(
cache_type), (path,), False)
self.storage.execute("INSERT INTO cache_{0}(path, value, time) "
"VALUES (?, ?, ?)".format(cache_type),
(path, convert_value, time))
def getcache(self, cache_type, path, exp):
path = self._sqlite_encode_path(path)
cached = self.storage.select("SELECT value FROM cache_{0} WHERE "
"path = ? AND time >= ?".format(
cache_type), (path, exp))
if cached is None:
return False, None
else:
return True, self._sqlite_decode(cached[0])
def hascache(self, cache_type, path):
path = self._sqlite_encode_path(path)
return self.storage.select("SELECT value FROM cache_{0} WHERE "
"path = ?".format(cache_type),
(path,)) is not None
def reset_expired_cache(self, cache_type, exp):
self.storage.execute("DELETE FROM cache_{0} WHERE time < ?".format(
cache_type), (exp,))
def reset_all_cache(self, cache_type):
self.storage.execute("DELETE FROM cache_{0}".format(cache_type))
def get_cached(self, cache_type, context):
"""return all values in a dictionary
example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
"""
ret = {}
for path, value, time in self.storage.select("SELECT * FROM cache_{0}"
"".format(cache_type),
only_one=False):
path = self._sqlite_decode_path(path)
value = self._sqlite_decode(value)
ret[path] = (value, time)
return ret

View file

@ -18,7 +18,7 @@
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.storage.sqlite3.storage import Cache from .cache import Cache
from tiramisu.setting import owners from tiramisu.setting import owners

View file

@ -44,9 +44,7 @@ class Values(object):
""" """
self.context = weakref.ref(context) self.context = weakref.ref(context)
# the storage type is dictionary or sqlite3 # the storage type is dictionary or sqlite3
import_lib = 'tiramisu.storage.{0}.value'.format(storage.storage) self._p_ = storage
self._p_ = __import__(import_lib, globals(), locals(), ['Values'],
).Values(storage)
def _getdefault(self, opt): def _getdefault(self, opt):
""" """