Skip to content
Snippets Groups Projects
Commit 9aa1c904 authored by tuhe's avatar tuhe
Browse files

fix of Cache and unicode

parent cb00db13
No related branches found
No related tags found
No related merge requests found
Metadata-Version: 2.1 Metadata-Version: 2.1
Name: unitgrade Name: unitgrade
Version: 0.1.30.5 Version: 0.1.30.6
Summary: A student homework/exam evaluation framework build on pythons unittest framework. Summary: A student homework/exam evaluation framework build on pythons unittest framework.
Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade Home-page: https://lab.compute.dtu.dk/tuhe/unitgrade
Author: Tue Herlau Author: Tue Herlau
......
...@@ -58,7 +58,7 @@ class DummyPipe: ...@@ -58,7 +58,7 @@ class DummyPipe:
self.type = type self.type = type
self.std_ = std_out_or_err self.std_ = std_out_or_err
self.queue = queue self.queue = queue
self.mute = False self.mute = mute
def write(self, message): def write(self, message):
if not self.mute: if not self.mute:
......
...@@ -43,7 +43,9 @@ parser.add_argument('--noprogress', action="store_true", help='Disable progres ...@@ -43,7 +43,9 @@ parser.add_argument('--noprogress', action="store_true", help='Disable progres
def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False, def evaluate_report_student(report, question=None, qitem=None, unmute=None, passall=None, ignore_missing_file=False,
show_tol_err=False, show_privisional=True, noprogress=None, show_tol_err=False, show_privisional=True, noprogress=None,
generate_artifacts=True): generate_artifacts=True,
show_errors_in_grade_mode=True # This is included for debugging purpose. Should always be True.
):
args = parser.parse_args() args = parser.parse_args()
if noprogress is None: if noprogress is None:
noprogress = args.noprogress noprogress = args.noprogress
...@@ -66,7 +68,7 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass ...@@ -66,7 +68,7 @@ def evaluate_report_student(report, question=None, qitem=None, unmute=None, pass
results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute and not noprogress, qitem=qitem, results, table_data = evaluate_report(report, question=question, show_progress_bar=not unmute and not noprogress, qitem=qitem,
verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute, verbose=False, passall=passall, show_expected=args.showexpected, show_computed=args.showcomputed,unmute=unmute,
show_tol_err=show_tol_err, show_tol_err=show_tol_err,
generate_artifacts=generate_artifacts) generate_artifacts=generate_artifacts, show_errors_in_grade_mode=show_errors_in_grade_mode)
if question is None and show_privisional: if question is None and show_privisional:
...@@ -109,7 +111,9 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa ...@@ -109,7 +111,9 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa
show_progress_bar=True, show_progress_bar=True,
show_tol_err=False, show_tol_err=False,
generate_artifacts=True, # Generate the artifact .json files. These are exclusively used by the dashboard. generate_artifacts=True, # Generate the artifact .json files. These are exclusively used by the dashboard.
big_header=True): big_header=True,
show_errors_in_grade_mode=True # Show test error during grading.
):
from unitgrade.version import __version__ from unitgrade.version import __version__
now = datetime.now() now = datetime.now()
...@@ -152,6 +156,7 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa ...@@ -152,6 +156,7 @@ def evaluate_report(report, question=None, qitem=None, passall=False, verbose=Fa
UTextResult.number = n UTextResult.number = n
UTextResult.nL = report.nL UTextResult.nL = report.nL
UTextResult.unmute = unmute # Hacky as well. UTextResult.unmute = unmute # Hacky as well.
UTextResult.show_errors_in_grade_mode = show_errors_in_grade_mode
UTextResult.setUpClass_time = q._cache.get(((q.__name__, 'setUpClass'), 'time'), 3) if hasattr(q, '_cache') and q._cache is not None else 3 UTextResult.setUpClass_time = q._cache.get(((q.__name__, 'setUpClass'), 'time'), 3) if hasattr(q, '_cache') and q._cache is not None else 3
......
...@@ -120,6 +120,9 @@ class Report: ...@@ -120,6 +120,9 @@ class Report:
relative_path = os.path.relpath(self._file(), root_dir) relative_path = os.path.relpath(self._file(), root_dir)
modules = os.path.normpath(relative_path[:-3]).split(os.sep) modules = os.path.normpath(relative_path[:-3]).split(os.sep)
relative_path = relative_path.replace("\\", "/") relative_path = relative_path.replace("\\", "/")
if relative_path.startswith(".."):
raise Exception("Bad relative path. setup failed. ", root_dir, self._file())
return root_dir, relative_path, modules return root_dir, relative_path, modules
def __init__(self, strict=False, payload=None): def __init__(self, strict=False, payload=None):
...@@ -351,6 +354,16 @@ class UTestCase(unittest.TestCase): ...@@ -351,6 +354,16 @@ class UTestCase(unittest.TestCase):
# print("Run called in test framework...", self._generate_artifacts) # print("Run called in test framework...", self._generate_artifacts)
if not self._generate_artifacts: if not self._generate_artifacts:
return super().run(result) return super().run(result)
print(result)
mute = False
if isinstance(result, UTextResult):
print(result.show_errors_in_grade_mode)
mute = not result.show_errors_in_grade_mode
else:
pass
# print("this' not a text result.")
# print(result.show_errors_in_grade_mode)
from unitgrade.artifacts import StdCapturing from unitgrade.artifacts import StdCapturing
from unitgrade.utils import DKPupDB from unitgrade.utils import DKPupDB
self._error_fed_during_run = [] # Initialize this to be empty. self._error_fed_during_run = [] # Initialize this to be empty.
...@@ -362,8 +375,8 @@ class UTestCase(unittest.TestCase): ...@@ -362,8 +375,8 @@ class UTestCase(unittest.TestCase):
_stdout = sys.stdout _stdout = sys.stdout
_stderr = sys.stderr _stderr = sys.stderr
# mute = True
std_capture = StdCapturing(stdout=sys.stdout, stderr=sys.stderr, db=db, mute=False) std_capture = StdCapturing(stdout=sys.stdout, stderr=sys.stderr, db=db, mute=mute)
state_ = None state_ = None
try: try:
...@@ -376,17 +389,13 @@ class UTestCase(unittest.TestCase): ...@@ -376,17 +389,13 @@ class UTestCase(unittest.TestCase):
# db.get('stdout') # db.get('stdout')
# db.get('stderr') # db.get('stderr')
# db.get("history") # db.get("history")
result_ = TestCase.run(self, result) result_ = TestCase.run(self, result)
from werkzeug.debug.tbtools import DebugTraceback, _process_traceback from werkzeug.debug.tbtools import DebugTraceback, _process_traceback
# What could be nice to upload? # What could be nice to upload?
# When the files are edited? # When the files are edited?
# When tests are run? # When tests are run?
# how to register it? (report, question, user...) # how to register it? (report, question, user...)
#
# print(result_._excinfo[0]) # print(result_._excinfo[0])
actual_errors = [] actual_errors = []
for test, err in self._error_fed_during_run: for test, err in self._error_fed_during_run:
...@@ -467,7 +476,7 @@ class UTestCase(unittest.TestCase): ...@@ -467,7 +476,7 @@ class UTestCase(unittest.TestCase):
for ll in data.contexts_by_lineno(file): for ll in data.contexts_by_lineno(file):
l = ll-1 l = ll-1
print(l, lines2[l]) # print(l, lines2[l])
if l < len(lines2) and lines2[l].strip() == garb: if l < len(lines2) and lines2[l].strip() == garb:
print("Got one.") print("Got one.")
rel = os.path.relpath(child, root) rel = os.path.relpath(child, root)
...@@ -858,12 +867,19 @@ class NotebookTestCase(UTestCase): ...@@ -858,12 +867,19 @@ class NotebookTestCase(UTestCase):
_nb = None _nb = None
@classmethod @classmethod
def setUpClass(cls) -> None: def setUpClass(cls) -> None:
f = cls._cache_file()
# print(f)
file = os.path.dirname(os.path.dirname(f)) + "/" + cls.notebook
# print(f"{f=}, {file=}, {cls.notebook=}")
# print(__file__)
print(os.path.curdir)
# print("cwd", os.getcwd())
with Capturing(): with Capturing():
# print(__file__) # print(__file__)
f = cls._cache_file() f = cls._cache_file()
# print(f) # print(f)
file = os.path.dirname(os.path.dirname(f)) + "/" + cls.notebook file = os.path.dirname(os.path.dirname(f)) + "/" + cls.notebook
cls._nb = importnb.Notebook.load_file(file) cls._nb = importnb.Notebook.load_argv(file + " -nograde")
@property @property
def nb(self): def nb(self):
......
...@@ -14,6 +14,7 @@ class UTextResult(unittest.TextTestResult): ...@@ -14,6 +14,7 @@ class UTextResult(unittest.TextTestResult):
unmute = False # Whether to redirect stdout. unmute = False # Whether to redirect stdout.
cc = None cc = None
setUpClass_time = 3 # Estimated time to run setUpClass in TestCase. Must be set externally. See key (("ClassName", "setUpClass"), "time") in _cache. setUpClass_time = 3 # Estimated time to run setUpClass in TestCase. Must be set externally. See key (("ClassName", "setUpClass"), "time") in _cache.
show_errors_in_grade_mode = True
def __init__(self, stream, descriptions, verbosity): def __init__(self, stream, descriptions, verbosity):
super().__init__(stream, descriptions, verbosity) super().__init__(stream, descriptions, verbosity)
...@@ -24,6 +25,19 @@ class UTextResult(unittest.TextTestResult): ...@@ -24,6 +25,19 @@ class UTextResult(unittest.TextTestResult):
self.printErrorList('ERROR', [(test, res['stderr']) for test, res in self.errors]) self.printErrorList('ERROR', [(test, res['stderr']) for test, res in self.errors])
self.printErrorList('FAIL', [(test, res['stderr']) for test, res in self.failures]) self.printErrorList('FAIL', [(test, res['stderr']) for test, res in self.failures])
def printErrorList(self, flavour, errors):
if not self.show_errors_in_grade_mode:
return
else:
super().printErrorList(flavour, errors)
#
# for test, err in errors:
# self.stream.writeln(self.separator1)
# self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
# self.stream.writeln(self.separator2)
# self.stream.writeln("%s" % err)
# self.stream.flush()
def addError(self, test, err): def addError(self, test, err):
super(unittest.TextTestResult, self).addError(test, err) super(unittest.TextTestResult, self).addError(test, err)
err = self.errors[-1][1] err = self.errors[-1][1]
...@@ -42,7 +56,6 @@ class UTextResult(unittest.TextTestResult): ...@@ -42,7 +56,6 @@ class UTextResult(unittest.TextTestResult):
if self.item_title_print is None: # In case the short description is not set either... if self.item_title_print is None: # In case the short description is not set either...
self.item_title_print = test.id() self.item_title_print = test.id()
self.cc_terminate(success=False) self.cc_terminate(success=False)
def addFailure(self, test, err): def addFailure(self, test, err):
...@@ -111,18 +124,21 @@ class UTextResult(unittest.TextTestResult): ...@@ -111,18 +124,21 @@ class UTextResult(unittest.TextTestResult):
# if self.show_progress_bar or True: # if self.show_progress_bar or True:
estimated_time = test.__class__._cache.get(((name, test._testMethodName), 'time'), 100) if hasattr(test.__class__, '_cache') else 4 estimated_time = test.__class__._cache.get(((name, test._testMethodName), 'time'), 100) if hasattr(test.__class__, '_cache') else 4
self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar) self.cc = ActiveProgress(t=estimated_time, title=self.item_title_print, show_progress_bar=self.show_progress_bar)
# else:
# print(self.item_title_print + ('.' * max(0, self.nL - 4 - len(self.item_title_print))), end="")
self._test = test self._test = test
# if not self.unmute:
self._stdout = sys.stdout # Redundant. remove later. self._stdout = sys.stdout # Redundant. remove later.
from unitgrade.utils import Logger from unitgrade.utils import Logger
sys.stdout = Logger(io.StringIO(), write_to_stdout=self.unmute) sys.stdout = Logger(io.StringIO(), write_to_stdout=self.unmute)
if not self.show_errors_in_grade_mode:
# print("Trying to hide the errors....", self.show_errors_in_grade_mode)
self._stderr = sys.stderr
sys.stderr = Logger(io.StringIO(), write_to_stdout=False)
def stopTest(self, test): def stopTest(self, test):
# if not self.unmute: # if not self.unmute:
buff = sys.stdout.log buff = sys.stdout.log
sys.stdout = self._stdout # redundant. sys.stdout = self._stdout # redundant.
if not self.show_errors_in_grade_mode:
sys.stderr = self._stderr
buff.close() buff.close()
from unitgrade.utils import Logger from unitgrade.utils import Logger
super().stopTest(test) super().stopTest(test)
...@@ -139,6 +155,7 @@ class UTextResult(unittest.TextTestResult): ...@@ -139,6 +155,7 @@ class UTextResult(unittest.TextTestResult):
self.cc = cc self.cc = cc
def _restoreStdout(self): # Used when setting up the test. def _restoreStdout(self): # Used when setting up the test.
if self._previousTestClass is None: if self._previousTestClass is None:
q_time = self.cc.terminate() q_time = self.cc.terminate()
......
...@@ -137,7 +137,7 @@ class ActiveProgress(): ...@@ -137,7 +137,7 @@ class ActiveProgress():
def terminate(self): def terminate(self):
if not self._running: if not self._running:
print("Stopping a progress bar which is not running (class unitgrade.utils.ActiveProgress") # print("Stopping a progress bar which is not running (class unitgrade.utils.ActiveProgress")
pass pass
# raise Exception("Stopping a stopped progress bar. ") # raise Exception("Stopping a stopped progress bar. ")
self._running = False self._running = False
...@@ -152,10 +152,7 @@ class ActiveProgress(): ...@@ -152,10 +152,7 @@ class ActiveProgress():
if self.mute_stdout: if self.mute_stdout:
import io import io
# from unitgrade.utils import Logger sys.stdout = self._stdout
sys.stdout = self._stdout #= sys.stdout
# sys.stdout = Logger(io.StringIO(), write_to_stdout=False)
return time.time() - self.time_started return time.time() - self.time_started
...@@ -308,9 +305,11 @@ def load_token(file_in): ...@@ -308,9 +305,11 @@ def load_token(file_in):
## Key/value store related. ## Key/value store related.
class DKPupDB: class DKPupDB:
DISABLE_DB = False
""" This key/value store store artifacts (associated with a specific question) in a dictionary. """ """ This key/value store store artifacts (associated with a specific question) in a dictionary. """
def __init__(self, artifact_file, use_pupdb=False, register_ephemeral=False): def __init__(self, artifact_file, use_pupdb=False, register_ephemeral=False):
# Make a double-headed disk cache thingy. # Make a double-headed disk cache thingy.
try:
self.dk = Cache(os.path.dirname(artifact_file)) # Start in this directory. self.dk = Cache(os.path.dirname(artifact_file)) # Start in this directory.
self.name_ = os.path.basename(artifact_file[:-5]) self.name_ = os.path.basename(artifact_file[:-5])
if self.name_ not in self.dk: if self.name_ not in self.dk:
...@@ -320,8 +319,14 @@ class DKPupDB: ...@@ -320,8 +319,14 @@ class DKPupDB:
if self.use_pupdb: if self.use_pupdb:
from pupdb.core import PupDB from pupdb.core import PupDB
self.db_ = PupDB(artifact_file) self.db_ = PupDB(artifact_file)
except Exception as e:
# print("Artifact file not readable. Possibly ")
self.DISABLE_DB = True
pass
def __setitem__(self, key, value): def __setitem__(self, key, value):
if self.DISABLE_DB:
return
if self.use_pupdb: if self.use_pupdb:
self.db_.set(key, value) self.db_.set(key, value)
with self.dk.transact(): with self.dk.transact():
...@@ -332,6 +337,8 @@ class DKPupDB: ...@@ -332,6 +337,8 @@ class DKPupDB:
self.dk[self.name_ + "-updated"] = True self.dk[self.name_ + "-updated"] = True
def __getitem__(self, item): def __getitem__(self, item):
if self.DISABLE_DB:
return "db disabled due to bad artifact file."
v = self.dk[self.name_][item] v = self.dk[self.name_][item]
if self.use_pupdb: if self.use_pupdb:
v2 = self.db_.get(item) v2 = self.db_.get(item)
...@@ -344,11 +351,17 @@ class DKPupDB: ...@@ -344,11 +351,17 @@ class DKPupDB:
# return self.db_.keys() # return self.db_.keys()
def set(self, item, value): # This one is deprecated. def set(self, item, value): # This one is deprecated.
if self.DISABLE_DB:
return
self[item] = value self[item] = value
def get(self, item, default=None): def get(self, item, default=None):
if self.DISABLE_DB:
return "artifact file not loaded."
return self[item] if item in self else default return self[item] if item in self else default
def __contains__(self, item): def __contains__(self, item):
if self.DISABLE_DB:
return False
return item in self.dk[self.name_] #keys() return item in self.dk[self.name_] #keys()
# return item in self.dk # return item in self.dk
__version__ = "0.1.30.6" __version__ = "0.1.30.8"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment