Source code for moptipy.utils.formatted_string

"""Strings that carry format information."""

from math import inf, isnan, nan
from typing import Final

from pycommons.types import check_int_range, type_error

#: the formatted string represents normal text
TEXT: Final[int] = 0
#: the formatted string represents a number
NUMBER: Final[int] = 1
#: the formatted string represents NaN, i.e., "not a number"
NAN: Final[int] = 2
#: the formatted string represents positive infinity
POSITIVE_INFINITY: Final[int] = 3
#: the formatted string represents negative infinity
NEGATIVE_INFINITY: Final[int] = 4
#: the formatted string represents a special character
SPECIAL: Final[int] = 5


[docs] class FormattedStr(str): """ A subclass of `str` capable of holding formatting information. This is a version of string that also stores format information that can, for example, be used by a text format driver when typesetting text. Instances of this class can also be used as normal strings and be printed to the console without any issue. However, they also hold information about whether the text should be :attr:`bold`, :attr:`italic`, or rendered in a monospace :attr:`code` font. Furthermore, if a number or numerical string is represented as a formatted string, the field :attr:`mode` will be non-zero. If it is `TEXT=1`, the string is a normal number, if it is `NAN=2`, the string is "nan", if it is `POSITIVE_INFINITY=3`, then the string is `inf`, and if it is `NEGATIVE_INFINITY=4`, the string is `-inf`. These values permit a text driver, for example, to replace the special numeric values with unicode constants or certain commands. It may also choose to replace floating point number of the form `1.5E23` with something like `1.5*10^23`. It can do so because a non-zero :attr:`mode` indicates that the string is definitely representing a number and that numbers in the string did not just occur for whatever other reason. If the field :attr:`mode` has value `SPECIAL=5`, then the text contains a special sequence, e.g., a unicode character outside of the normal range. Then, the text format driver can render this character as a special entity. """ #: should this string be formatted in bold face? bold: bool #: should this string be formatted in italic face? italic: bool #: should this string be formatted in code face? code: bool #: the special mode: `TEXT`, `NUMBER`, `NAN`, `POSITIVE_INFINITY`, #: `NEGATIVE_INFINITY`, or `SPECIAL` mode: int def __new__(cls, value, bold: bool = False, italic: bool = False, # noqa code: bool = False, mode: int = TEXT): # noqa """ Construct the formatted string. :param value: the string value :param bold: should the format be bold face? :param italic: should the format be italic face? :param code: should the format be code face? :param mode: the mode of the formatted string :returns: an instance of `FormattedStr` """ if not isinstance(value, str): raise type_error(value, "value", str) if not isinstance(bold, bool): raise type_error(bold, "bold", bool) if not isinstance(italic, bool): raise type_error(italic, "italic", bool) if not isinstance(code, bool): raise type_error(code, "code", bool) check_int_range(mode, "mode", TEXT, SPECIAL) ret = super().__new__(cls, value) ret.bold = bold ret.italic = italic ret.code = code ret.mode = mode return ret
[docs] @staticmethod def add_format(s: str, bold: bool = False, italic: bool = False, code: bool = False) -> str: """ Add the given format to the specified string. :param s: the string :param bold: should the format be bold face? :param italic: should the format be italic face? :param code: should the format be code face? >>> from typing import cast >>> st = "abc" >>> type(st) <class 'str'> >>> fs = cast(FormattedStr, FormattedStr.add_format(st, bold=True)) >>> type(fs) <class 'moptipy.utils.formatted_string.FormattedStr'> >>> fs.bold True >>> fs.italic False >>> fs = cast(FormattedStr, FormattedStr.add_format(fs, italic=True)) >>> fs.bold True >>> fs.italic True >>> fs.mode 0 """ if isinstance(s, FormattedStr): bold = bold or s.bold italic = italic or s.italic code = code or s.code if (bold != s.bold) or (italic != s.italic) or (code != s.code): return FormattedStr(s, bold, italic, code, s.mode) return s if not isinstance(s, str): raise type_error(s, "s", str) if bold or italic or code: return FormattedStr(s, bold, italic, code, TEXT) return s
[docs] @staticmethod def number(number: int | float | str | None) -> "FormattedStr": """ Create a formatted string representing a number. :param number: the original number or numeric string :return: the formatted string representing it >>> FormattedStr.number(inf) 'inf' >>> FormattedStr.number(inf) is _PINF True >>> FormattedStr.number(inf).mode 3 >>> FormattedStr.number(-inf) '-inf' >>> FormattedStr.number(-inf) is _NINF True >>> FormattedStr.number(-inf).mode 4 >>> FormattedStr.number(nan) 'nan' >>> FormattedStr.number(nan) is _NAN True >>> FormattedStr.number(nan).mode 2 >>> FormattedStr.number(123) '123' >>> FormattedStr.number(123e3) '123000.0' >>> FormattedStr.number(123e3).mode 1 """ if not isinstance(number, int | str): if isinstance(number, float): if isnan(number): return _NAN if number >= inf: return _PINF if number <= -inf: return _NINF else: raise type_error(number, "number", (float, int, str)) return FormattedStr(str(number), False, False, False, NUMBER)
[docs] @staticmethod def special(text: str) -> "FormattedStr": r""" Create a special string. A special string has mode `SPECIAL` set and notifies the text format that special formatting conversion, e.g., a translation from the character "alpha" (\u03b1) to `\alpha` is required. :param text: the text >>> FormattedStr.special("bla") 'bla' >>> FormattedStr.special("bla").mode 5 """ return FormattedStr(text, False, False, False, SPECIAL)
#: the constant for not-a-number _NAN: Final[FormattedStr] = FormattedStr(str(nan), False, False, False, NAN) #: the constant for positive infinity _PINF: Final[FormattedStr] = FormattedStr(str(inf), False, False, False, POSITIVE_INFINITY) #: the constant for negative infinity _NINF: Final[FormattedStr] = FormattedStr(str(-inf), False, False, False, NEGATIVE_INFINITY)