Source code for pibootctl.userstr

# Copyright (c) 2020 Canonical Ltd.
# Copyright (c) 2020 Dave Jones <dave@waveform.org.uk>
#
# This file is part of pibootctl.
#
# pibootctl 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 3 of the License, or
# (at your option) any later version.
#
# pibootctl 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 pibootctl.  If not, see <https://www.gnu.org/licenses/>.

"""
The :mod:`pibootctl.userstr` module provides the :class:`UserStr` class which
represents unparsed user input on the command line.

The module also provides a variety of functions for converting input (either
from JSON, YAML, or other structured formats, or from unparsed
:class:`UserStr`) into common types (:func:`to_bool`, :func:`to_int`,
:func:`to_str`, etc).

.. autoclass:: UserStr

.. autofunction:: to_bool

.. autofunction:: to_int

.. autofunction:: to_float

.. autofunction:: to_str

.. autofunction:: to_list
"""

import gettext

_ = gettext.gettext


[docs]class UserStr(str): """ Type used to represent a value expressed as a string on the command line. In other words, any value bearing this type is a string representation of some other type (possibly :class:`str`, :class:`int`, :data:`None`, etc.) Primarily used by various conversion routines (:func:`to_bool`, :func:`to_str`, etc.) to determine whether a value is a string parsed from some serialization format (like JSON or YAML) which should be treated as a string literal. .. note:: The blank :class:`UserStr` is special in that it *always* represents :data:`None` in conversions. """
[docs]def to_bool(s): """ Converts the :class:`UserStr` (or other type) *s* to a :class:`bool`. Various "typical" string representations of true and false are accepted including "true", "yes", and "on", along with their counter-parts "false", "no", and "off". Literal :data:`None` passes through unchanged, and a blank :class:`UserStr` will convert to :data:`None`. """ if s is None: return None elif isinstance(s, UserStr): try: return { '': None, 'auto': None, 'true': True, 'yes': True, 'on': True, '1': True, 'y': True, 'false': False, 'no': False, 'off': False, '0': False, 'n': False, }[s.strip().lower()] except KeyError: raise ValueError( _('{value} is not a valid bool').format(value=s)) return bool(s)
[docs]def to_int(s): """ Converts the :class:`UserStr` (or other type) *s* to a :class:`int`. As with all :class:`UserStr` conversions, blank string inputs are converted to :data:`None`, and literal :data:`None` passes through unchanged. Otherwise, decimal integers and hexi-decimal integers prefixed with "0x" are accepted. """ if s is None: return None elif isinstance(s, str): if isinstance(s, UserStr): if not s: return None s = s.strip().lower() if s[:2] == '0x': return int(s, base=16) return int(s)
[docs]def to_float(s): """ Converts the :class:`UserStr` (or other type) *s* to a :class:`float`. As with all :class:`UserStr` conversions, blank string inputs are converted to :data:`None`, and literal :data:`None` passes through unchanged. Otherwise, typical floating point values (optionally prefixed with sign, optionally suffixed with an exponent) are accepted. """ if s is None: return None elif isinstance(s, str): if isinstance(s, UserStr): if not s: return None return float(s)
[docs]def to_str(s): """ Converts the :class:`UserStr` (or other type) *s* to a :class:`str`. Blank :class:`UserStr` are converted to :data:`None`, and literal :data:`None` passes through unchanged. Everything else is simply passed to the :class:`str` constructor. """ if s is None: return None elif isinstance(s, UserStr): if not s: return None else: return s.strip() return str(s)
[docs]def to_list(s, sep=','): """ Converts the :class:`UserStr` (or other type) *s* to a :class:`list` based on the separator character *sep* (which defaults to ","). Blank :class:`UserStr` are converted to :data:`None`, and literal :data:`None` passes through unchanged. Everything else is passed to the :class:`list` constructor. This ensures that the result is always a unique reference. """ if s is None: return None elif isinstance(s, UserStr): if not s: return None else: s = s.strip() if isinstance(s, str): if sep in s: return [elem.strip() for elem in s.split(sep)] else: return [s] return list(s)