Source code for configupdater.option

"""Options are the ultimate mean of configuration inside a configuration value.

They are always associated with a :attr:`~Option.key` (or the name of the configuration
parameter) and a :attr:`~Option.value`.

Options can also have multi-line values that are usually interpreted as a list of
values.

When editing configuration files with ConfigUpdater, a handy way of setting a multi-line
(or comma separated value) for an specific option is to use the
:meth:`~Option.set_values` method.
"""
import sys
from typing import TYPE_CHECKING, Optional, TypeVar, Union, cast

if sys.version_info[:2] >= (3, 9):  # pragma: no cover
    List = list
    Dict = dict
else:
    from typing import List  # pragma: no cover

if TYPE_CHECKING:
    from .section import Section

from .block import Block

Value = Union["Option", str]
T = TypeVar("T", bound="Option")


[docs]class Option(Block): """Option block holding a key/value pair. Attributes: key (str): name of the key value (str): stored value updated (bool): indicates name change or a new section """ def __init__( self, key: str, value: Optional[str] = None, container: Optional["Section"] = None, delimiter: str = "=", space_around_delimiters: bool = True, line: Optional[str] = None, ): super().__init__(container=container) self._key = key self._values: List[Optional[str]] = [] if value is None else [value] self._value_is_none = value is None self._delimiter = delimiter self._value: Optional[str] = None # will be filled after join_multiline_value self._updated = False self._multiline_value_joined = False self._space_around_delimiters = space_around_delimiters if line: super().add_line(line) if value is not None: self.value = value
[docs] def add_value(self, value: Optional[str]): """PRIVATE: this function is not part of the public API of Option. It is only used internally by other classes of the package during parsing. """ self._value_is_none = value is None self._values.append(value)
[docs] def add_line(self, line: str): """PRIVATE: this function is not part of the public API of Option. It is only used internally by other classes of the package during parsing. """ super().add_line(line) self.add_value(line.strip())
def _join_multiline_value(self): if not self._multiline_value_joined and not self._value_is_none: # do what `_join_multiline_value` in ConfigParser would do self._value = "\n".join(self._values).rstrip() self._multiline_value_joined = True def __str__(self) -> str: if not self.updated: return super().__str__() if self._value is None: return "{}{}".format(self._key, "\n") if self._space_around_delimiters: # no space is needed if we use multi-line arguments suffix = "" if str(self._value).startswith("\n") else " " delim = " {}{}".format(self._delimiter, suffix) else: delim = self._delimiter return "{}{}{}{}".format(self._key, delim, self._value, "\n") def __repr__(self) -> str: return f"<Option: {self._key} = {self.value!r}>" def _instantiate_copy(self: T) -> T: """Will be called by :meth:`Block.__deepcopy__`""" self._join_multiline_value() return self.__class__( self._key, self._value, container=None, delimiter=self._delimiter, space_around_delimiters=self._space_around_delimiters, ) @property def section(self) -> "Section": return cast("Section", self.container) @property def key(self) -> str: """Key string associated with the option. Please notice that the option key is normalized with :meth:`~configupdater.document.Document.optionxform`. When the option object is :obj:`detached <configupdater.block.Block.detach>`, this method will raise a :obj:`NotAttachedError`. """ return self.section.document.optionxform(self._key) @key.setter def key(self, value: str): self._join_multiline_value() self._key = value self._updated = True @property def raw_key(self) -> str: """Equivalent to :obj:`key`, but before applying :meth:`optionxform`.""" return self._key @property def value(self) -> Optional[str]: self._join_multiline_value() return self._value @value.setter def value(self, value: str): self._updated = True self._multiline_value_joined = True self._value = value self._values = [value]
[docs] def set_values(self, values: List[str], separator="\n", indent=4 * " "): """Sets the value to a given list of options, e.g. multi-line values Args: values (iterable): sequence of values separator (str): separator for values, default: line separator indent (str): indentation depth in case of line separator """ values = list(values).copy() self._updated = True self._multiline_value_joined = True self._values = cast(List[Optional[str]], values) if separator == "\n": values = [""] + values separator = separator + indent self._value = separator.join(values)