Commit 7ed4c4a7 authored by Karel Koci's avatar Karel Koci 🤘

euci: overload default get and set methods to support types

This drops previous get_t and set_t methods in favor of overloaded get
and set methods.
You can optionally add keyword argument dtype to get method to ensure
that returned data is of specified type. For now only str, int and bool
are supported.
set methods now detects value argument type and behaves according to it.
In general it just converts value to string but in case of bool it
replaces it with defined value.
parent 4b9ab0a6
Pipeline #48748 passed with stage
in 18 seconds
...@@ -43,8 +43,55 @@ class EUci(Uci): ...@@ -43,8 +43,55 @@ class EUci(Uci):
__BOOLEAN_TRUE = "1" __BOOLEAN_TRUE = "1"
__BOOLEAN_FALSE = "0" __BOOLEAN_FALSE = "0"
def __init__(self, *args, **kwargs): def get(self, *args, **kwargs):
super().__init__(*args, **kwargs) """Get configuration value.
Up to three positional arguments are expected. Those are uci "config"
"section" and "option" in this order.
Following additional optional keywords arguments are available:
dtype: data type to be returned. Currently supported are: str, bool and
int. If you don't specify this then it defaults to str. If value
cannot be converted to specified type then it raises ValueError.
When requested value is not found then this raises UciExceptionNotFound.
"""
dtype = kwargs.get('dtype', str)
value = super().get(*args)
if dtype == str:
return value
if dtype == bool:
value = value.lower()
if value not in self.__BOOLEAN_VALUES:
raise ValueError
return self.__BOOLEAN_VALUES[value]
if dtype == int:
return int(value)
raise EUciExceptionUnsupportedType(dtype)
def set(self, *args, **kwargs):
"""Set configuration value.
Up to three positional arguments specifying configuration can be
specified. Those are "config", "section" and "option". Option does not
have to be specified and in such case section value is set. Last
positional argument (third or fourth one) is value to be set. This
function automatically detect that argument type and sets value
according to that. Supported types are: str, bool and int.
If provided value is not of any of these types then it is converted to
string.
It is suggested to always explicitly type last positional argument to
ensure correct type. That is in case of boolean for example:
set("foo", "fee", "faa", bool(value))
"""
dtype = type(args[-1])
if dtype == bool:
value = self.__BOOLEAN_TRUE if args[-1] else self.__BOOLEAN_FALSE
else:
# This implements handler for str and int type as well as fallback
value = str(args[-1])
super().set(*args[:-1], value)
def get_default(self, *args, default=None, **kwargs): def get_default(self, *args, default=None, **kwargs):
"""Wrap UCI get method with additional check for missing config value. """Wrap UCI get method with additional check for missing config value.
...@@ -56,15 +103,13 @@ class EUci(Uci): ...@@ -56,15 +103,13 @@ class EUci(Uci):
return default return default
def get_boolean(self, *args, **kwargs): def get_boolean(self, *args, **kwargs):
"""Returns given UCI config as a boolean. """This is obsolete! Please use instead: set(config, section, option, dtype=bool)
Returns given UCI config as a boolean.
Value '0', 'no', 'off', 'false' or 'disabled' is returned as False. Value '0', 'no', 'off', 'false' or 'disabled' is returned as False.
Value '1' , 'yes', 'on', 'true' or 'enabled' is returned as True. Value '1' , 'yes', 'on', 'true' or 'enabled' is returned as True.
ValueError is raised on any other value. ValueError is raised on any other value.
""" """
value = self.get(*args, **kwargs) return self.get(*args, dtype=bool, **kwargs)
if value.lower() not in self.__BOOLEAN_VALUES:
raise ValueError
return self.__BOOLEAN_VALUES[value.lower()]
def get_boolean_default(self, *args, default=False, **kwargs): def get_boolean_default(self, *args, default=False, **kwargs):
"""Returns given UCI config as a boolean. """Returns given UCI config as a boolean.
...@@ -79,15 +124,17 @@ class EUci(Uci): ...@@ -79,15 +124,17 @@ class EUci(Uci):
return bool(default) return bool(default)
def set_boolean(self, *args): def set_boolean(self, *args):
"""Sets boolean value to given UCI config. """This is obsolete! Please use instead: set(config, section, option, value, bool(value))
Sets boolean value to given UCI config.
""" """
self.set(*args[:-1], self.__BOOLEAN_TRUE if args[-1] else self.__BOOLEAN_FALSE) self.set(*args[:-1], bool(args[-1]))
def get_integer(self, *args, **kwargs): def get_integer(self, *args, **kwargs):
"""Returns given UCI config as an integer. """This is obsolete! Please use instead: set(config, section, option, dtype=int)
Returns given UCI config as an integer.
Raises ValueError if config value can't be converted to int. Raises ValueError if config value can't be converted to int.
""" """
return int(self.get(*args, **kwargs)) return self.get(*args, dtype=int, **kwargs)
def get_integer_default(self, *args, default=0, **kwargs): def get_integer_default(self, *args, default=0, **kwargs):
"""Returns given UCI config as an integer. """Returns given UCI config as an integer.
...@@ -100,45 +147,10 @@ class EUci(Uci): ...@@ -100,45 +147,10 @@ class EUci(Uci):
return int(default) return int(default)
def set_integer(self, *args): def set_integer(self, *args):
"""Sets integer to given UCI config. """This is obsolete! Please use instead: set(config, section, option, value, int(value))
""" Sets integer to given UCI config.
self.set(*args[:-1], str(args[-1]))
def get_t(self, dtype, *args, **kwargs):
"""Returns given UCI config typed as Python type in dtype.
This is wrapper on top of other EUci get_* methods. Note that this is
not magic. It only provides you with convinient way to use Python type
to call specific get_* method.
Returns value of requested type.
All exceptions it can raise depends on requested type but it also
raises one additional exception: EUciExceptionUnsupportedType
""" """
tpmethods = { self.set(*args[:-1], int(args[-1]))
str: self.get,
bool: self.get_boolean,
int: self.get_integer,
}
if dtype not in tpmethods:
raise EUciExceptionUnsupportedType(dtype)
return tpmethods[dtype](*args, **kwargs)
def set_t(self, *args, **kwargs):
"""Sets given value to given UCI config while detecting type.
Only supported types are those supported by set_* methods. If provided
value has type which is not supported or recognized then it is
converted to string. To ensure that you are setting correct type you
should always type it with call. That is for example:
set_t("foo", "fee", "faa", bool(value))
"""
tpmethods = {
bool: self.set_boolean,
int: self.set_integer,
}
dtype = type(args[-1])
if dtype in tpmethods:
tpmethods[dtype](*args, **kwargs)
else:
self.set(*args[:-1], str(args[-1]), **kwargs)
class EUciException(UciException): class EUciException(UciException):
......
...@@ -18,8 +18,28 @@ import pytest ...@@ -18,8 +18,28 @@ import pytest
import euci import euci
def test_boolean_get(tmpdir): def test_get_string(tmpdir):
'Test get_boolean' 'Test get for no dtype (string in default)'
tmpdir.join('test').write("""
config str 'str'
option foo 'value'
""")
u = euci.EUci(confdir=tmpdir.strpath)
assert u.get('test', 'str', 'foo') == 'value'
def test_set_string(tmpdir):
'Test set for type string'
tmpdir.join('test').write("""
config testing 'testing'
""")
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set('test', 'testing', 'foo', 'value')
assert u.get('test', 'testing', 'foo') == 'value'
def test_get_boolean(tmpdir):
'Test get for dtype boolean'
tmpdir.join('test').write(""" tmpdir.join('test').write("""
config integer 'integer' config integer 'integer'
option true '1' option true '1'
...@@ -42,50 +62,50 @@ config bled 'bled' ...@@ -42,50 +62,50 @@ config bled 'bled'
option false 'disabled' option false 'disabled'
""") """)
u = euci.EUci(confdir=tmpdir.strpath) u = euci.EUci(confdir=tmpdir.strpath)
assert u.get_boolean('test', 'integer', 'true') assert u.get('test', 'integer', 'true', dtype=bool)
assert u.get_boolean('test', 'word', 'true') assert u.get('test', 'word', 'true', dtype=bool)
assert u.get_boolean('test', 'state', 'true') assert u.get('test', 'state', 'true', dtype=bool)
assert u.get_boolean('test', 'bool', 'true') assert u.get('test', 'bool', 'true', dtype=bool)
assert u.get_boolean('test', 'bled', 'true') assert u.get('test', 'bled', 'true', dtype=bool)
assert not u.get_boolean('test', 'integer', 'false') assert not u.get('test', 'integer', 'false', dtype=bool)
assert not u.get_boolean('test', 'word', 'false') assert not u.get('test', 'word', 'false', dtype=bool)
assert not u.get_boolean('test', 'state', 'false') assert not u.get('test', 'state', 'false', dtype=bool)
assert not u.get_boolean('test', 'bool', 'false') assert not u.get('test', 'bool', 'false', dtype=bool)
assert not u.get_boolean('test', 'bled', 'false') assert not u.get('test', 'bled', 'false', dtype=bool)
def test_boolean_set(tmpdir): def test_set_boolean(tmpdir):
'Test set_boolean' 'Test set for type boolean'
tmpdir.join('test').write(""" tmpdir.join('test').write("""
config testing 'testing' config testing 'testing'
""") """)
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath) u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set_boolean('test', 'testing', 'true', True) u.set('test', 'testing', 'true', True)
u.set_boolean('test', 'testing', 'false', False) u.set('test', 'testing', 'false', False)
assert u.get('test', 'testing', 'true') == '1' assert u.get('test', 'testing', 'true') == '1'
assert u.get('test', 'testing', 'false') == '0' assert u.get('test', 'testing', 'false') == '0'
def test_integer_get(tmpdir): def test_get_integer(tmpdir):
'Test get_integer' 'Test get for dtype int'
tmpdir.join('test').write(""" tmpdir.join('test').write("""
config integer 'integer' config integer 'integer'
option plus '42' option plus '42'
option minus '-42' option minus '-42'
""") """)
u = euci.EUci(confdir=tmpdir.strpath) u = euci.EUci(confdir=tmpdir.strpath)
assert u.get_integer('test', 'integer', 'plus') == 42 assert u.get('test', 'integer', 'plus', dtype=int) == 42
assert u.get_integer('test', 'integer', 'minus') == -42 assert u.get('test', 'integer', 'minus', dtype=int) == -42
def test_integer_set(tmpdir): def test_set_integer(tmpdir):
'Test set_integer' 'Test set for type int'
tmpdir.join('test').write(""" tmpdir.join('test').write("""
config testing 'testing' config testing 'testing'
""") """)
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath) u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set_integer('test', 'testing', 'plus', 42) u.set('test', 'testing', 'plus', 42)
u.set_integer('test', 'testing', 'minus', -42) u.set('test', 'testing', 'minus', -42)
assert u.get('test', 'testing', 'plus') == '42' assert u.get('test', 'testing', 'plus') == '42'
assert u.get('test', 'testing', 'minus') == '-42' assert u.get('test', 'testing', 'minus') == '-42'
...@@ -98,83 +118,5 @@ config testing 'testing' ...@@ -98,83 +118,5 @@ config testing 'testing'
option two '1' option two '1'
""") """)
with euci.EUci(confdir=tmpdir.strpath) as u: with euci.EUci(confdir=tmpdir.strpath) as u:
assert not u.get_boolean('test', 'testing', 'one') assert not u.get('test', 'testing', 'one', dtype=bool)
assert u.get_boolean('test', 'testing', 'two') assert u.get('test', 'testing', 'two', dtype=bool)
def test_get_t_string(tmpdir):
'Test get_t method for str type'
tmpdir.join('test').write("""
config str 'str'
option foo 'value'
""")
u = euci.EUci(confdir=tmpdir.strpath)
v = u.get_t(str, 'test', 'str', 'foo')
assert type(v) == str
assert v == 'value'
def test_get_t_boolean(tmpdir):
'Test get_t method for boolean type'
tmpdir.join('test').write("""
config bool 'bool'
option true 'true'
option false 'false'
""")
u = euci.EUci(confdir=tmpdir.strpath)
v1 = u.get_t(bool, 'test', 'bool', 'true')
assert type(v1) == bool
assert v1
v2 = u.get_t(bool, 'test', 'bool', 'false')
assert type(v2) == bool
assert not v2
def test_get_t_int(tmpdir):
'Test get_t method for int type'
tmpdir.join('test').write("""
config integer 'integer'
option plus '42'
option minus '-42'
""")
u = euci.EUci(confdir=tmpdir.strpath)
v1 = u.get_t(int, 'test', 'integer', 'plus')
assert v1 == 42
assert type(v1) == int
v2 = u.get_t(int, 'test', 'integer', 'minus')
assert v2 == -42
assert type(v2) == int
def test_set_t(tmpdir):
'Test set_integer'
tmpdir.join('test').write("""
config testing 'testing'
""")
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set_t('test', 'testing', 'foo', 'value')
assert u.get('test', 'testing', 'foo') == 'value'
def test_set_t_boolean(tmpdir):
'Test set_t method for bool type'
tmpdir.join('test').write("""
config testing 'testing'
""")
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set_t('test', 'testing', 'true', True)
u.set_t('test', 'testing', 'false', False)
assert u.get('test', 'testing', 'true') == '1'
assert u.get('test', 'testing', 'false') == '0'
def test_set_t_integer(tmpdir):
'Test set_integer'
tmpdir.join('test').write("""
config testing 'testing'
""")
u = euci.EUci(savedir=tmpdir.mkdir('save').strpath, confdir=tmpdir.strpath)
u.set_t('test', 'testing', 'plus', 42)
u.set_t('test', 'testing', 'minus', -42)
assert u.get('test', 'testing', 'plus') == '42'
assert u.get('test', 'testing', 'minus') == '-42'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment