Source code for sboxUv2.display.output


# !SECTION! General setup


# !SUBSECTION! Importing classes needed for tests

from collections import defaultdict

# !SUBSECTION! Importing pretty printing utilities from Rich 

from rich.console import Console
from rich.theme import Theme


# !SUBSECTION! Importing tools to run a timer 

import datetime
import time

from math import floor


# !SUBSECTION! Setting up global variables

from ..config import SECTION_TEMPLATES
CONSOLE = Console(theme=Theme({}, inherit=False))
ONGOING_EXPERIMENT = None




# !SECTION! The main pretty printing function 

[docs] def pprint(*args): result = "" all_pretty = True for x in args: if hasattr(x, "__rich_str__"): result += x.__rich_str__() elif isinstance(x, (dict, defaultdict)): result = "{ " for k in sorted(x.keys()): result += "[bold]{}[/bold]: {}, ".format(k, x[k]) result = result[:-2] + " }" else: result += str(x) if not isinstance(x, (str)): all_pretty = False result += ", " result = result[:-2] # ditching superfluous ", " if all_pretty: CONSOLE.print(result) else: print(result)
# !SECTION! Handling sections in the output # !SUBSECTION! The Chronograph class
[docs] class Chronograph: def __init__(self, title): self.title = title self.start_time = datetime.datetime.now() def __str__(self): elapsed_time = datetime.datetime.now() - self.start_time tot_secs = floor(elapsed_time.total_seconds()) days = floor(tot_secs / 86400) hours = floor((tot_secs % 86400) / 3600) minutes = floor((tot_secs % 3600) / 60) seconds = (tot_secs % 60) + elapsed_time.total_seconds() - tot_secs return "\"{}\" lasted {}s ({})".format( self.title, elapsed_time.total_seconds(), "{:d}d {:02d}h {:02d}m {:5.03f}s".format( days, hours, minutes, seconds )) def __rich_str__(self): elapsed_time = datetime.datetime.now() - self.start_time tot_secs = floor(elapsed_time.total_seconds()) days = floor(tot_secs / 86400) hours = floor((tot_secs % 86400) / 3600) minutes = floor((tot_secs % 3600) / 60) seconds = (tot_secs % 60) + elapsed_time.total_seconds() - tot_secs return "[blue]{}[/blue] lasted [bold]{}[/bold]s [gray]({})[/gray]".format( self.title, elapsed_time.total_seconds(), "{:d}d {:02d}h {:02d}m {:5.03f}s".format( days, hours, minutes, seconds ))
# !SUBSECTION! Starting and finishing sections
[docs] class Experiment: """The purpose of this class is to simplify the task of generating a pretty output in a terminal that contains more automatically derived information. To use it, simply rely on the `with` statement: Example: with Experiment("your experiment title"): section("the first part") <your code> section("the second part (which is bigger)") subsection("the first part of the second part") <your code> When running your code, the title of the experiment will be displayed, and the sections titles will be highlighted for ease of reading. The runtime of each section and subsection is also displayed at the end of each of these. """ def __init__(self, title : str): self.title = title self.sections_counters = [0] self.section_timer = None CONSOLE.print(SECTION_TEMPLATES[0].format(self.title)) self.global_timer = Chronograph("[bold]{}[/bold]".format(self.title))
[docs] def section(self, title : str) -> None: if self.sections_counters[0] > 0: pprint(self.section_timer) self.sections_counters = [self.sections_counters[0] + 1] self.section_timer = Chronograph(title) CONSOLE.print(SECTION_TEMPLATES[1].format( self.sections_counters[0], title))
[docs] def subsection(self, title : str) -> None: if len(self.sections_counters) == 1: self.sections_counters.append(1) else: self.sections_counters = [ self.sections_counters[0], self.sections_counters[1] + 1 ] CONSOLE.print(SECTION_TEMPLATES[2].format( self.sections_counters[0], self.sections_counters[1], title ))
def __enter__(self): global ONGOING_EXPERIMENT ONGOING_EXPERIMENT = self def __exit__(self, exception_type, exception_value, traceback): if self.sections_counters[0] > 0: pprint(self.section_timer) pprint(self.global_timer)
[docs] def section(title : str) -> None: global ONGOING_EXPERIMENT if ONGOING_EXPERIMENT == None: raise Exception("sections can only be used within an Experiment") ONGOING_EXPERIMENT.section(title)
[docs] def subsection(title : str) -> None: global ONGOING_EXPERIMENT if ONGOING_EXPERIMENT == None: raise Exception("subsections can only be used within an Experiment") ONGOING_EXPERIMENT.subsection(title)
# !TODO! Write a function that uses python functions' __name__ attribute and the Chronograph object to easily compare the time efficiency different functions on the same input. For example, we want to write # ! # !compare_execution_time([f1, f2], common_inputs) # ! # ! If x=(x0, x1) and f has two inputs, then f(*x) is the same as f(x0, x1)