Source code for gemseo.utils.string_tools

# -*- coding: utf-8 -*-
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program 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 program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Contributors:
#    INITIAL AUTHORS - initial API and implementation and/or initial
#                           documentation
#        :author: Matthias De Lozzo
#        :author: Antoine Dechaume
#    OTHER AUTHORS   - MACROSCOPIC CHANGES
"""
Pretty string utils
===================
"""
from collections import namedtuple
from contextlib import contextmanager
from copy import deepcopy
from typing import Any, List, Optional

from gemseo.utils.py23_compat import PY2

if PY2:
    from collections import Iterable, Mapping
else:
    from collections.abc import Iterable, Mapping

# to store the raw ingredients of a string to be formatted later
MessageLine = namedtuple("MessageLine", "str_format level args kwargs")


[docs]def pretty_repr( obj, # type: Any **kwargs # type: Any ): # type: (...)-> str """String representation of an object. Args: obj: The object to represent. Returns: A pretty string representation. """ delimiter = kwargs.get("delimiter", ", ") if isinstance(obj, Mapping): return delimiter.join( ["{}={}".format(key, repr(val)) for key, val in sorted(obj.items())] ) if isinstance(obj, Iterable): return delimiter.join([str(val) for val in obj]) return repr(obj)
[docs]class MultiLineString(object): """Multi-line string lazy evaluator. The creation of the string is postponed to when an instance is stringified through the __repr__ method. This is mainly used for logging complex strings or objects where the string evaluation cost may be avoided when the logging level dismisses a logging message. A __add__ method is defined to allow the "+" operator between two instances, that implements the concatenation of two MultiLineString. If the other instance is not MultiLineString, it is first converted to string using its __str__ method and then added as a new line in the result. """ INDENTATION = " " * 3 DEFAULT_LEVEL = 0 def __init__( self, lines=None, # type: Optional[Iterable[MessageLine]] ): # type: (...) -> None if lines is None: self.__lines = [] else: self.__lines = list(lines) self.__level = None self.reset()
[docs] def add( self, str_format, # type: str *args, # type: Any **kwargs # type: Any ): # type: (...)-> None """Add a line. Args: str_format: The string to be process by the format() method. args: The args passed to the format() method. kwargs: The kwargs passed to the format() method. """ self.__lines.append(MessageLine(str_format, self.__level, args, kwargs))
@property def lines(self): # type: (...) -> List[MessageLine] """The strings composing the lines.""" return self.__lines
[docs] def reset(self): # type: (...) -> None """Reset the indentation.""" self.__level = self.DEFAULT_LEVEL
[docs] def indent(self): # type: (...) -> None """Increase the indentation.""" self.__level += 1
[docs] def dedent(self): # type: (...) -> None """Decrease the indentation.""" if self.__level > 0: self.__level -= 1
[docs] def replace( self, old, # type: str new, # type: str ): # type: (...) -> MultiLineString """Return a new MultiLineString with all occurrences of old replaced by new. Args: old: The sub-string to be replaced. new: The sub-string to be replaced with. Returns: The MultiLineString copy with replaced occurences. """ repl_msg = [] for line in self.__lines: new_str = line.str_format.replace(old, new) repl_msg.append(MessageLine(new_str, line.level, line.args, line.kwargs)) return MultiLineString(repl_msg)
def __repr__(self): # type: (...) -> str lines = [] for line in self.__lines: str_format = self.INDENTATION * line.level + line.str_format lines.append(str_format.format(*line.args, **line.kwargs)) return "\n".join(lines) def __add__( self, other # type: Any ): # type: (...) -> MultiLineString if isinstance(other, MultiLineString): return MultiLineString(self.lines + other.lines) out = deepcopy(self) out.add(str(other)) return out
[docs] @classmethod @contextmanager def offset(cls): # type: (...) -> None cls.DEFAULT_LEVEL += 1 try: yield finally: cls.DEFAULT_LEVEL -= 1