# Copyright (c) 2019 Cyso < development [at] cyso . com >
#
# This file is part of omniconf, a.k.a. python-omniconf .
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 3.0 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see
# <http://www.gnu.org/licenses/>.
from typing import Any, Callable, Sequence, Union, overload
CallableType = Callable[..., Any]
[docs]
class SeparatorSequence(Sequence[str]):
"""
Splits the given string using the given separator, and provides a
the result with a read-only Sequence interface.
"""
def __init__(self, string: str, separator: str) -> None:
self.lst = string.split(separator)
@overload
def __getitem__(self, index: int) -> str: ... # pragma: nocover
@overload
def __getitem__(self, index: slice) -> Sequence[str]: ... # pragma: nocover
def __getitem__(self, index: Union[int, slice]) -> Union[str, Sequence[str]]:
return self.lst.__getitem__(index)
def __len__(self) -> int:
return self.lst.__len__()
def __str__(self) -> str:
return self.lst.__str__()
def __repr__(self) -> str:
return self.lst.__repr__()
[docs]
def separator_sequence(separator: str) -> Callable[[Union[str, Sequence[str]]], Sequence[str]]:
"""
Returns a function that parses a string value, separates it into parts and
stores it as a read-only sequence:
.. code-block:: python
parser = separator_sequence(",")
print(parser("a,b,c"))
# ['a', 'b', 'c']
If the input value is already a sequence (but not a string), the value is
returned as is. The sequence is an instance of :class:`.SeparatorSequence`,
and can be used as one would normally use a (read-only) tuple or list.
"""
def factory(value: Union[str, Sequence[str]]) -> Sequence[str]:
if isinstance(value, Sequence) and not isinstance(value, str):
return value
return SeparatorSequence(value, separator)
return factory
[docs]
def string_or_false(value: str) -> Union[str, bool]:
"""
Returns the given value as-is, unless the values equals "False". In that
case, boolean False is returned.
"""
if value == "False":
return False
return value
[docs]
def string_bool(value: Union[str, bool]) -> Union[str, bool]:
"""
Returns False if the value is Falsish or "False", True if value is "True",
or the original value otherwise.
"""
if isinstance(value, bool):
return value
if not value or value == "False":
return False
if value == "True":
return True
return value
[docs]
def enum(values: Sequence[str]) -> Callable[[str], str]:
"""
Returns the original value if it is present in values, otherwise raises a
RuntimeError.
.. code-block:: python
enum_func = enum(["foo", "bar"])
print(enum_func("foo"))
# "foo"
print(enum_func("baz"))
# ...
# RuntimeError: Invalid value specified, must be one of: foo, bar
"""
def factory(value: str) -> str:
if value not in values:
raise RuntimeError("Invalid value specified, must be one of: {}".format(", ".join(values)))
return value
return factory