From 1260aa5a8382ca00e39d855e9f610366d41ccba8 Mon Sep 17 00:00:00 2001 From: Tim Vieira Date: Sun, 21 Jul 2013 08:30:15 -0400 Subject: [PATCH] Address Issue #51 - showing errors in "Changes" updated doctests, caught some cases of missing clear error. --- run-doctests.py | 7 ++++ src/Dyna/Backend/Python/interpreter.py | 24 +++++++---- src/Dyna/Backend/Python/repl.py | 57 +++++++++++++++++++++----- test/error-handling/basics.dynadoc | 10 +++-- test/error-handling/clearing.dynadoc | 4 ++ test/error-handling/clearing2.dynadoc | 4 ++ test/repl/boolean-aggregators.dynadoc | 7 +++- test/repl/colon-equals.dynadoc | 6 +++ test/repl/equals-errors.dynadoc | 5 +++ test/repl/list.dynadoc | 4 ++ test/repl/recursion-limit.dynadoc | 9 +++- 11 files changed, 113 insertions(+), 24 deletions(-) diff --git a/run-doctests.py b/run-doctests.py index 18b5a85..ee8d18d 100755 --- a/run-doctests.py +++ b/run-doctests.py @@ -48,6 +48,13 @@ print 'Doctests' print '========' for x in sorted(path('test').glob("*/*.dynadoc")): + + if '/ptb.dynadoc' in x: + continue + + if '/known-failures/' in x: + continue + print x, sys.stdout.flush() with file(x) as f: diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index 33a9e41..83e2707 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -123,6 +123,7 @@ class Interpreter(object): return self.chart[fn].insert(args) def delete(self, item, val, ruleix, variables): + self.clear_error(item) self.emit(item, val, ruleix, variables, delete=True) def emit(self, item, val, ruleix, variables, delete): @@ -438,6 +439,8 @@ class Interpreter(object): print 'Rule %s not found.' % idx return + self.clear_error(rule) + # remove $rule if hasattr(rule, 'item'): self.delete(rule.item, true, ruleix=None, variables=None) @@ -565,12 +568,6 @@ class Interpreter(object): def dump_errors(self, out=None): if out is None: out = sys.stdout - # We only dump the error chart if it's non empty. - if not self.error and not self.uninitialized_rules: - return - print >> out - print >> out, red % 'Errors' - print >> out, red % '======' # separate errors into aggregation errors and update handler errors I = defaultdict(lambda: defaultdict(list)) @@ -583,8 +580,21 @@ class Interpreter(object): if h is None: I[item.fn][type(e)].append((item, val, e)) else: + if h.rule.index not in self.rules: + # TODO: clear all errors pertaining to a rule at + # push-time. This is a temporary filter, which is fine + # for now. + continue E[h.rule][type(e)].append((item, val, e)) + # We only dump the error chart if it's non empty. + if not I and not E and not self.uninitialized_rules: + return + + print >> out + print >> out, red % 'Errors' + print >> out, red % '======' + # aggregation errors for r in sorted(I, key=lambda r: r.index): print >> out, 'Error(s) aggregating %s:' % r @@ -599,7 +609,7 @@ class Interpreter(object): # errors pertaining to rules for r in sorted(E, key=lambda r: r.index): - print >> out, 'Error(s) in rule:', r.span + print >> out, 'Error(s) in rule %s:' % r.index, r.span print >> out for line in r.src.split('\n'): print >> out, ' ', line diff --git a/src/Dyna/Backend/Python/repl.py b/src/Dyna/Backend/Python/repl.py index b7d427a..217fe1f 100644 --- a/src/Dyna/Backend/Python/repl.py +++ b/src/Dyna/Backend/Python/repl.py @@ -8,7 +8,7 @@ to help. """ import re, os, cmd, readline -from utils import ip, lexer, subst, drepr, _repr, get_module +from utils import ip, lexer, subst, drepr, _repr, get_module, yellow from stdlib import topython, todyna from errors import DynaCompilerError from config import dotdynadir @@ -16,6 +16,10 @@ from errors import show_traceback import load, post +from interpreter import Rule +from contextlib import contextmanager + + class REPL(cmd.Cmd, object): def __init__(self, interp, hist=dotdynadir / 'dyna.hist'): @@ -86,12 +90,13 @@ class REPL(cmd.Cmd, object): except ValueError: print 'Please specify an integer. Type `help retract_rule` to read more.' else: - changes = self.interp.retract_rule(idx) - if changes is None: - print 'List available by typing `rules`' - print - else: - self._changed(changes) + with self.error_tracker(): + changes = self.interp.retract_rule(idx) + if changes is None: + print 'List available by typing `rules`' + print + else: + self._changed(changes) def do_exit(self, _): """ @@ -259,12 +264,42 @@ class REPL(cmd.Cmd, object): print 'new rule(s) were not added to program.' print else: - new_rules = self.interp.load_plan(src) - changed = self.interp.run_agenda() - if show_changed: - self._changed(changed) + with self.error_tracker(): + new_rules = self.interp.load_plan(src) + changed = self.interp.run_agenda() + if show_changed: + self._changed(changed) return (new_rules, changed) + @contextmanager + def error_tracker(self): + errors_before = self.interp.error.copy() + yield + if errors_before.items() != self.interp.error.items(): + e = set(errors_before) | set(self.interp.error) + new_errors = 0 + cleared_errors = 0 + for k in e: + was = k in errors_before + now = k in self.interp.error + if isinstance(k, Rule): + k = 'rule index %s' % k.index + if was and not now: + #print 'cleared error at `%s`.' % k + cleared_errors += 1 + elif not was and now: + #print 'new error at `%s`.' % k + new_errors += 1 + if new_errors and cleared_errors: + print yellow % '>>>', '%s new errors, %s errors cleared. Type `sol` for details.\n' \ + % (new_errors, cleared_errors) + elif new_errors: + print yellow % '>>>', '%s new errors. Type `sol` for details.\n' \ + % (new_errors,) + elif cleared_errors: + print yellow % '>>>', '%s errors cleared.\n' \ + % (cleared_errors,) + def _changed(self, changed): if not changed: return diff --git a/test/error-handling/basics.dynadoc b/test/error-handling/basics.dynadoc index 202701a..d553c75 100644 --- a/test/error-handling/basics.dynadoc +++ b/test/error-handling/basics.dynadoc @@ -32,6 +32,8 @@ f(1,2) = 2. f(2,1) = 1. f(2,2) = 2. +>>> 5 new errors. Type `sol` for details. + > sol Solution @@ -61,25 +63,25 @@ Error(s) aggregating a/1: Error(s) aggregating d/0: TypeError: `d`: unsupported operand type(s) for *: 'NoneType' and 'int' -Error(s) in rule: +Error(s) in rule 1: a += 1/b. ZeroDivisionError: when `b` = 0 division by zero a += (1 / b=0)=?. -Error(s) in rule: +Error(s) in rule 2: c += "" + b. TypeError: when `b` = 0 cannot concatenate 'str' and 'int' objects c += ("" + b=0)=?. -Error(s) in rule: +Error(s) in rule 4: b := e/0. ZeroDivisionError: when `e` = 0 division by zero b := (e=0 / 0)=?. -Error(s) in rule: +Error(s) in rule 5: a += e/0. ZeroDivisionError: when `e` = 0 diff --git a/test/error-handling/clearing.dynadoc b/test/error-handling/clearing.dynadoc index 1d7e82d..6f41162 100644 --- a/test/error-handling/clearing.dynadoc +++ b/test/error-handling/clearing.dynadoc @@ -20,6 +20,8 @@ b = 1. > a += 1/d. +>>> 1 new errors. Type `sol` for details. + > sol Solution @@ -48,6 +50,8 @@ a = 2. b = 2. d = 1. +>>> 1 errors cleared. + % Looks good. > sol diff --git a/test/error-handling/clearing2.dynadoc b/test/error-handling/clearing2.dynadoc index aeee31d..44481f3 100644 --- a/test/error-handling/clearing2.dynadoc +++ b/test/error-handling/clearing2.dynadoc @@ -6,6 +6,8 @@ d = 0. > a += 1 / d. +>>> 1 new errors. Type `sol` for details. + > rules Rules @@ -35,3 +37,5 @@ Changes ======= a = 1.0. d = 1. + +>>> 1 errors cleared. diff --git a/test/repl/boolean-aggregators.dynadoc b/test/repl/boolean-aggregators.dynadoc index fbfc66c..19eb59b 100644 --- a/test/repl/boolean-aggregators.dynadoc +++ b/test/repl/boolean-aggregators.dynadoc @@ -6,6 +6,8 @@ Changes ======= e = $error. +>>> 1 new errors. Type `sol` for details. + > sol Solution @@ -24,7 +26,7 @@ Changes ======= e = null. - +>>> 1 errors cleared. > a :- false. @@ -49,6 +51,8 @@ Changes b = $error. c = "horse". +>>> 1 new errors. Type `sol` for details. + > sol @@ -72,6 +76,7 @@ Changes b = true. c = true. +>>> 1 errors cleared. > f(1,2). f(2,2). diff --git a/test/repl/colon-equals.dynadoc b/test/repl/colon-equals.dynadoc index 377fa4a..0534d9e 100644 --- a/test/repl/colon-equals.dynadoc +++ b/test/repl/colon-equals.dynadoc @@ -9,6 +9,9 @@ a(1) = 1. a(2) = 2. b = $error. +>>> 1 new errors. Type `sol` for details. + + > sol Solution @@ -42,6 +45,9 @@ Changes ======= b = 0. +>>> 1 errors cleared. + + > a(1) := 2. a(2) := 2. b := 0. b := a(X). Changes diff --git a/test/repl/equals-errors.dynadoc b/test/repl/equals-errors.dynadoc index 3da43e5..3ae3945 100644 --- a/test/repl/equals-errors.dynadoc +++ b/test/repl/equals-errors.dynadoc @@ -15,6 +15,8 @@ Changes ======= a = $error. +>>> 1 new errors. Type `sol` for details. + > sol Solution @@ -34,6 +36,9 @@ Changes ======= a = 2. +>>> 1 errors cleared. + + > sol Solution diff --git a/test/repl/list.dynadoc b/test/repl/list.dynadoc index 78e2da5..fa91a02 100644 --- a/test/repl/list.dynadoc +++ b/test/repl/list.dynadoc @@ -1,5 +1,7 @@ > x = cons(1, 2). +>>> 1 new errors. Type `sol` for details. + > sol Solution empty. @@ -16,6 +18,8 @@ Failed to initialize rule: > retract_rule 0 +>>> 1 errors cleared. + > s set= Y for Y in [3,2,1,[2,1],&f(1)]. Changes diff --git a/test/repl/recursion-limit.dynadoc b/test/repl/recursion-limit.dynadoc index 1dbe2d7..0c7cb94 100644 --- a/test/repl/recursion-limit.dynadoc +++ b/test/repl/recursion-limit.dynadoc @@ -12,8 +12,13 @@ Changes ======= a = $error. +>>> 1 new errors. Type `sol` for details. + + > b = a + 1. +>>> 1 new errors. Type `sol` for details. + > rules Rules @@ -30,7 +35,7 @@ a = $error. Errors ====== -Error(s) in rule: +Error(s) in rule 0: f(X) = f(X-1). RuntimeError: when `f(-323)` = null @@ -50,6 +55,8 @@ Changes ======= a = null. +>>> 2 errors cleared. + > sol Solution empty. \ No newline at end of file -- 2.50.1