Source code for psi4.driver.p4util.text
#
# @BEGIN LICENSE
#
# Psi4: an open-source quantum chemistry software package
#
# Copyright (c) 2007-2024 The Psi4 Developers.
#
# The copyrights for code used from other parties are included in
# the corresponding files.
#
# This file is part of Psi4.
#
# Psi4 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, version 3.
#
# Psi4 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 Psi4; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# @END LICENSE
#
"""Module with utility classes and functions related
to data tables and text.
"""
__all__ = [
"banner",
"find_approximate_string_matches",
"levenshtein",
"message_box",
]
from typing import List, Optional
from psi4 import core
from .. import constants
[docs]
def banner(text: str, type: int = 1, width: int = 35, strNotOutfile: bool = False) -> Optional[str]:
"""Format `text` into a banner style and print or return it.
Parameters
----------
text
String to be emphasized.
type
Style 1 has minimum three-line height. Style 2 has minimum one-light
height.
width
Minimum length of banner string.
strNotOutfile
Controls mode of return.
Returns
-------
str
If *strNotOutfile* is True, return string.
None
If *strNotOutfile* is False, print it to output file.
"""
lines = text.split('\n')
max_length = 0
for line in lines:
max_length = max(len(line), max_length)
max_length = max(width, max_length)
null = ''
if type == 1:
banner = ' //' + null.center(max_length, '>') + '//\n'
for line in lines:
banner += ' //' + line.center(max_length) + '//\n'
banner += ' //' + null.center(max_length, '<') + '//\n'
if type == 2:
banner = ''
for line in lines:
banner += (' ' + line + ' ').center(max_length, '=')
if strNotOutfile:
return banner
else:
core.print_out(banner)
[docs]
def levenshtein(seq1: str, seq2:str) -> int:
"""Compute the Levenshtein distance between two strings.
Parameters
----------
seq1
First string.
seq2
Second string.
"""
oneago = None
thisrow = list(range(1, len(seq2) + 1)) + [0]
for x in range(len(seq1)):
twoago, oneago, thisrow = oneago, thisrow, [0] * len(seq2) + [x + 1]
for y in range(len(seq2)):
delcost = oneago[y] + 1
addcost = thisrow[y - 1] + 1
subcost = oneago[y - 1] + (seq1[x] != seq2[y])
thisrow[y] = min(delcost, addcost, subcost)
return thisrow[len(seq2) - 1]
[docs]
def find_approximate_string_matches(seq1: str, options: List[str], max_distance: int) -> List[str]:
"""Find list of approximate (within `max_distance`) matches to string `seq1` among `options`.
Parameters
----------
seq1
Target string to look for near matches to.
options
Alternatives among which to look for near matches to `seq1`.
max_distance
Maximum Levenshtein distance from `seq1` to return.
"""
return [seq2 for seq2 in options if (levenshtein(seq1, seq2) <= max_distance)]
[docs]
def message_box(message: str, max_width: int = 80, min_width: int = 30) -> str:
"""Put a message string into a box for extra attention.
Parameters
----------
message
Message string to be boxed.
max_width
Maximal character width of the box.
min_width
Minimal character width of the box.
Returns
-------
str
Box containing the message as a multiline string.
"""
from textwrap import wrap
# ensure box is within min/max boundaries
msg = message.splitlines()
max_line = len(max(msg, key=len))
box_width = max(min(max_width, max_line), min_width)
error_str = []
error_str.append('\n!' + '-' * box_width + '--!\n')
error_str.append('!' + ' ' * box_width + ' !\n')
fmt = "! {:" + str(box_width) + "} !\n"
for line in msg[:]:
error_str.extend([fmt.format(x) for x in wrap(line, box_width, subsequent_indent=" ")])
error_str.append('!' + ' ' * box_width + ' !\n')
error_str.append('!' + '-' * box_width + '--!\n')
error_str = ''.join(error_str)
return error_str