diff --git a/driver.py b/driver.py index 19f8d06..1af6201 100755 --- a/driver.py +++ b/driver.py @@ -55,7 +55,7 @@ def load_relations(): # Naming the relation relname = i[:-4] - print ("Loading relation %s with name %s..." % (i, relname)) + print("Loading relation %s with name %s..." % (i, relname)) rels[relname] = relation.Relation.load_csv('%s%s' % (examples_path, i)) print('done') @@ -102,45 +102,45 @@ def execute_tests(): else: f_bad += 1 - print (colorize("Resume of the results", COLOR_CYAN)) + print(colorize("Resume of the results", COLOR_CYAN)) - print (colorize("Query tests", COLOR_MAGENTA)) - print ("Total test count: %d" % q_tot) - print ("Passed tests: %d" % q_good) + print(colorize("Query tests", COLOR_MAGENTA)) + print("Total test count: %d" % q_tot) + print("Passed tests: %d" % q_good) if q_bad > 0: - print (colorize("Failed tests count: %d" % q_bad, COLOR_RED)) + print(colorize("Failed tests count: %d" % q_bad, COLOR_RED)) - print (colorize("Python tests", COLOR_MAGENTA)) - print ("Total test count: %d" % py_tot) - print ("Passed tests: %d" % py_good) + print(colorize("Python tests", COLOR_MAGENTA)) + print("Total test count: %d" % py_tot) + print("Passed tests: %d" % py_good) if py_bad > 0: - print (colorize("Failed tests count: %d" % py_bad, COLOR_RED)) + print(colorize("Failed tests count: %d" % py_bad, COLOR_RED)) - print (colorize("Execute Python tests", COLOR_MAGENTA)) - print ("Total test count: %d" % ex_tot) - print ("Passed tests: %d" % ex_good) + print(colorize("Execute Python tests", COLOR_MAGENTA)) + print("Total test count: %d" % ex_tot) + print("Passed tests: %d" % ex_good) if ex_bad > 0: - print (colorize("Failed tests count: %d" % ex_bad, COLOR_RED)) + print(colorize("Failed tests count: %d" % ex_bad, COLOR_RED)) - print (colorize("Execute fail tests", COLOR_MAGENTA)) - print ("Total test count: %d" % f_tot) - print ("Passed tests: %d" % f_good) + print(colorize("Execute fail tests", COLOR_MAGENTA)) + print("Total test count: %d" % f_tot) + print("Passed tests: %d" % f_good) if f_bad > 0: - print (colorize("Failed tests count: %d" % f_bad, COLOR_RED)) + print(colorize("Failed tests count: %d" % f_bad, COLOR_RED)) - print (colorize("Total results", COLOR_CYAN)) + print(colorize("Total results", COLOR_CYAN)) if f_bad + q_bad + py_bad + ex_bad == 0: - print (colorize("No failed tests", COLOR_GREEN)) + print(colorize("No failed tests", COLOR_GREEN)) return 0 else: - print (colorize("There are %d failed tests" % + print(colorize("There are %d failed tests" % (f_bad + py_bad + q_bad + ex_bad), COLOR_RED)) return 1 def run_exec_test(testname): '''Runs a python test, which executes code directly rather than queries''' - print ("Running python test: " + colorize(testname, COLOR_MAGENTA)) + print("Running python test: " + colorize(testname, COLOR_MAGENTA)) glob = rels.copy() exp_result = {} @@ -149,19 +149,19 @@ def run_exec_test(testname): try: exec(expr, glob) # Evaluating the expression - print (colorize('Test passed', COLOR_GREEN)) + print(colorize('Test passed', COLOR_GREEN)) return True except Exception as e: - print (colorize('ERROR', COLOR_RED)) - print (colorize('=====================================', COLOR_RED)) + print(colorize('ERROR', COLOR_RED)) + print(colorize('=====================================', COLOR_RED)) traceback.print_exc(file=sys.stdout) - print (colorize('=====================================', COLOR_RED)) + print(colorize('=====================================', COLOR_RED)) return False def run_py_test(testname): '''Runs a python test, which evaluates expressions directly rather than queries''' - print ("Running expression python test: " + + print("Running expression python test: " + colorize(testname, COLOR_MAGENTA)) exp_result = None result = None @@ -174,21 +174,21 @@ def run_py_test(testname): exp_result = eval(expr, rels) if result == exp_result: - print (colorize('Test passed', COLOR_GREEN)) + print(colorize('Test passed', COLOR_GREEN)) return True except: pass - print (colorize('ERROR', COLOR_RED)) - print (colorize('=====================================', COLOR_RED)) - print ("Expected %s" % exp_result) - print ("Got %s" % result) - print (colorize('=====================================', COLOR_RED)) + print(colorize('ERROR', COLOR_RED)) + print(colorize('=====================================', COLOR_RED)) + print("Expected %s" % exp_result.pretty_string(tty=True)) + print("Got %s" % result.pretty_string(tty=True)) + print(colorize('=====================================', COLOR_RED)) return False def run_fail_test(testname): '''Runs a test, which executes a query that is supposed to fail''' - print ("Running fail test: " + colorize(testname, COLOR_MAGENTA)) + print("Running fail test: " + colorize(testname, COLOR_MAGENTA)) query = readfile('%s%s.fail' % (tests_path, testname)).strip() test_succeed = True @@ -216,9 +216,9 @@ def run_fail_test(testname): pass if test_succeed: - print (colorize('Test passed', COLOR_GREEN)) + print(colorize('Test passed', COLOR_GREEN)) else: - print (colorize('Test failed (by not raising any exception)', COLOR_RED)) + print(colorize('Test failed (by not raising any exception)', COLOR_RED)) return test_succeed def run_test(testname): @@ -228,7 +228,7 @@ def run_test(testname): testname.result The query will be executed both unoptimized and optimized''' - print ("Running test: " + colorize(testname, COLOR_MAGENTA)) + print("Running test: " + colorize(testname, COLOR_MAGENTA)) query = None expr = None @@ -254,32 +254,32 @@ def run_test(testname): c_result = eval(c_expr, rels) if (o_result == result_rel) and (result == result_rel) and (c_result == result_rel): - print (colorize('Test passed', COLOR_GREEN)) + print(colorize('Test passed', COLOR_GREEN)) return True except Exception as inst: traceback.print_exc(file=sys.stdout) - print (inst) + print(inst) pass - print (colorize('ERROR', COLOR_RED)) - print ("Query: %s -> %s" % (query, expr)) - print ("Optimized query: %s -> %s" % (o_query, o_expr)) - print (colorize('=====================================', COLOR_RED)) - print (colorize("Expected result", COLOR_GREEN)) - print (result_rel) - print (colorize("Result", COLOR_RED)) - print (result) - print (colorize("Optimized result", COLOR_RED)) - print (o_result) - print (colorize("optimized result match %s" % + print(colorize('ERROR', COLOR_RED)) + print("Query: %s -> %s" % (query, expr)) + print("Optimized query: %s -> %s" % (o_query, o_expr)) + print(colorize('=====================================', COLOR_RED)) + print(colorize("Expected result", COLOR_GREEN)) + print(result_rel.pretty_string(tty=True)) + print(colorize("Result", COLOR_RED)) + print(result.pretty_string(tty=True)) + print(colorize("Optimized result", COLOR_RED)) + print(o_result.pretty_string(tty=True)) + print(colorize("optimized result match %s" % str(result_rel == o_result), COLOR_MAGENTA)) - print (colorize("result match %s" % + print(colorize("result match %s" % str(result == result_rel), COLOR_MAGENTA)) - print (colorize('=====================================', COLOR_RED)) + print(colorize('=====================================', COLOR_RED)) return False if __name__ == '__main__': - print ("-> Starting testsuite for relational") + print("-> Starting testsuite for relational") load_relations() - print ("-> Starting tests") + print("-> Starting tests") exit(execute_tests()) diff --git a/relational/relation.py b/relational/relation.py index 6ec03d2..4524834 100644 --- a/relational/relation.py +++ b/relational/relation.py @@ -412,22 +412,49 @@ class Relation: def __len__(self): return len(self.content) - def __str__(self): - m_len = [len(i) for i in self.header] # Maximum lenght string + def __str__(self) -> str: + return self.pretty_string(tty=False) + + def pretty_string(self, tty: bool) -> str: + ''' + Returns a printable string. + + If tty is enabled, it will attempt to add ANSI color codes + ''' + c = lambda i, ansi: i + if not tty: + colorize = c + else: + from os import isatty + if isatty(1) and isatty(2): + try: + from xtermcolor import colorize # type: ignore + except ModuleNotFoundError: + colorize = c + else: + colorize = c + m_len = [len(i) + 2 for i in self.header] # Maximum lenght string for f in self.content: - for col, i in enumerate(str(val) for val in f): - if len(i) > m_len[col]: - m_len[col] = len(i) + for col, k in enumerate(str(val) for val in f): + if len(k) + 2 > m_len[col]: + m_len[col] = len(k) + 2 res = "" - for f, attr in enumerate(self.header): - res += attr.ljust(2 + m_len[f]) + for j, attr in enumerate(self.header): + res += colorize(attr.ljust(m_len[j]), ansi=3) for r in self.content: res += "\n" - for col, i in enumerate(str(val) for val in r): - res += i.ljust(2 + m_len[col]) + for col, i in enumerate(r): + cell = str(i).ljust(m_len[col]) + if isinstance(i, (int, float)): + cell = colorize(cell, ansi=4) + elif i is None: + cell = colorize(cell, ansi=1) + elif not isinstance(i, str): + cell = colorize(cell, ansi=15) + res += cell return res diff --git a/relational_readline/linegui.py b/relational_readline/linegui.py index 6471bc3..fea09cb 100644 --- a/relational_readline/linegui.py +++ b/relational_readline/linegui.py @@ -165,12 +165,19 @@ def help(command: str) -> None: p = command.split(' ', 1) if len(p) == 1: print( - 'HELP command\n' + 'HELP [command]\n' + '\n' + 'Comments are obtained starting with a ;\n' + '\n' 'To execute a query:\n' '[relation =] query\n' + '\n' 'If the 1st part is omitted, the result will be stored in the relation last_.\n' + '\n' 'To prevent from printing the relation, append a ; to the end of the query.\n' + '\n' 'To insert relational operators, type _OPNAME, they will be internally replaced with the correct symbol.\n' + '\n' 'Rember: completion is enabled and can be very helpful if you can\'t remember something.' ) return @@ -301,7 +308,7 @@ def exec_query(command: str) -> None: if printrel: print() - print(result) + print(result.pretty_string(tty=True)) ui.relations[relname] = result