Source code for jube2.util.output
# JUBE Benchmarking Environment
# Copyright (C) 2008-2024
# Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre
# http://www.fz-juelich.de/jsc/jube
#
# This program 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
# any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""ASCII and string output generators"""
from __future__ import (print_function,
unicode_literals,
division)
import jube2.conf
import textwrap
import copy
import sys
import xml.etree.ElementTree as ET
[docs]def text_boxed(text):
"""Create an ASCII boxed version of text."""
box = "#" * jube2.conf.DEFAULT_WIDTH
for line in text.split("\n"):
box += "\n"
lines = ["# {0}".format(element) for element in
textwrap.wrap(line.strip(), jube2.conf.DEFAULT_WIDTH - 2)]
if len(lines) == 0:
box += "#"
else:
box += "\n".join(lines)
box += "\n" + "#" * jube2.conf.DEFAULT_WIDTH
return box
[docs]def text_line():
"""Return a horizonal ASCII line"""
return "#" * jube2.conf.DEFAULT_WIDTH
[docs]def text_table(entries_ext, use_header_line=False, indent=1, align_right=True,
auto_linebreak=True, colw=None, style="pretty", separator=None,
transpose=False):
"""Create a ASCII based table.
entries must contain a list of lists, use_header_line can be used to
mark the first entry as title.
Return the ASCII table
"""
if style != "pretty":
auto_linebreak = False
use_header_line = False
indent = 0
# Transpose data entries if needed
if transpose:
entries = list(zip(*entries_ext))
use_header_line = False
else:
entries = copy.deepcopy(entries_ext)
max_length = list()
table_str = ""
header_line_used = not use_header_line
# calculate needed maxlength
for item in entries:
for i, text in enumerate(item):
if i > len(max_length) - 1:
max_length.append(0)
if style != "csv":
for line in text.splitlines():
max_length[i] = max(max_length[i], len(line))
if auto_linebreak:
max_length[i] = min(max_length[i],
jube2.conf.MAX_TABLE_CELL_WIDTH)
if colw is not None:
for i, maxl in enumerate(max_length):
if i < len(colw):
max_length[i] = max(maxl, colw[i])
# fill cells
for item in entries:
# Wrap text
wraps = list()
for text in item:
if auto_linebreak:
lines = list()
for line in text.splitlines():
lines += \
textwrap.wrap(line, jube2.conf.MAX_TABLE_CELL_WIDTH)
wraps.append(lines)
else:
if style == "pretty":
wraps.append(text.splitlines())
else:
wraps.append([text.replace("\n", " ")])
grow = True
height = 0
while grow:
grow = False
line_str = " " * indent
if style == "pretty":
line_str += "| "
for i, wrap in enumerate(wraps):
grow = grow or len(wrap) > height + 1
if len(wrap) > height:
text = wrap[height]
else:
text = ""
if align_right and height == 0:
align = ">"
else:
align = "<"
line_str += \
("{0:" + align + str(max_length[i]) + "s}").format(text)
if i < len(max_length) - 1:
if separator is None:
line_str += " | " if style == "pretty" else ","
else:
line_str += separator
if style == "pretty":
line_str += " |"
line_str += "\n"
table_str += line_str
height += 1
if not header_line_used:
# Create title separator line
table_str += " " * indent + "|-"
for i, cell_length in enumerate(max_length):
table_str += "-" * cell_length
if i < len(max_length) - 1:
table_str += "-|-"
table_str += "-|\n"
header_line_used = True
return table_str
[docs]def print_loading_bar(current_cnt, all_cnt, wait_cnt=0, error_cnt=0):
"""Show a simple loading animation"""
width = jube2.conf.DEFAULT_WIDTH - 10
cnt = dict()
if all_cnt > 0:
cnt["done_cnt"] = (current_cnt * width) // all_cnt
cnt["wait_cnt"] = (wait_cnt * width) // all_cnt
cnt["error_cnt"] = (error_cnt * width) // all_cnt
else:
cnt["done_cnt"] = 0
cnt["wait_cnt"] = 0
cnt["error_cnt"] = 0
# shrink cnt if there was some rounding issue
for key in ("wait_cnt", "error_cnt"):
if (cnt[key] > 0) and (width < sum(cnt.values())):
cnt[key] = max(0, width - sum([cnt[k] for k in cnt if k != key]))
# fill up medium_cnt if there was some rounding issue
if (current_cnt + wait_cnt + error_cnt == all_cnt) and \
(sum(cnt.values()) < width):
for key in ("wait_cnt", "error_cnt", "done_cnt"):
if cnt[key] > 0:
cnt[key] += width - sum(cnt.values())
break
cnt["todo_cnt"] = width - sum(cnt.values())
bar_str = "\r{0}{1}{2}{3} ({4:3d}/{5:3d})".format("#" * cnt["done_cnt"],
"0" * cnt["wait_cnt"],
"E" * cnt["error_cnt"],
"." * cnt["todo_cnt"],
current_cnt, all_cnt)
sys.stdout.write(bar_str)
sys.stdout.flush()
[docs]def element_tree_tostring(element, encoding=None):
"""A more encoding friendly ElementTree.tostring method"""
class Dummy(object):
"""Dummy class to offer write method for etree."""
def __init__(self):
self._data = list()
@property
def data(self):
"""Return data"""
return self._data
def write(self, *args):
"""Simulate write"""
self._data.append(*args)
file_dummy = Dummy()
ET.ElementTree(element).write(file_dummy, encoding)
return "".join(dat.decode(encoding) for dat in file_dummy.data)