Source code for grader.decorators

import os
from .utils import read_code, setDescription
from functools import wraps

[docs]def test_decorator(decorator): """ Decorator for test decorators. This makes the decorator work testcases decorated with :func:`grader.wrappers.test_cases`. """ @wraps(decorator) def _inner(f): if isinstance(f, list) or isinstance(f, tuple): return tuple(decorator(func) for func in f) else: return decorator(f) return _inner ### Hooks
[docs]def before_test(action): """ Decorator for a pre-hook on a tested function. Makes the tester execute the function `action` before running the decorated test. """ @test_decorator def _inner_decorator(test_function): hooks = get_setting(test_function, "pre-hooks") + (action,) set_setting(test_function, "pre-hooks", hooks) return test_function return _inner_decorator
[docs]def after_test(action): """ Decorator for a post-hook on a tested function. Makes the tester execute the function `action` after running the decorated test. """ @test_decorator def _inner_decorator(test_function): hooks = get_setting(test_function, "post-hooks") + (action,) set_setting(test_function, "post-hooks", hooks) return test_function return _inner_decorator
[docs]def timeout(seconds): """ Decorator for a test. Indicates how long the test can run. """ @test_decorator def _inner(test_function): set_setting(test_function, "timeout", seconds) return test_function return _inner
@test_decorator
[docs]def set_description(d): """ Decorator for setting the description of a test. Example usage:: @grader.test @grader.set_description("New description") def a_test_case(m): ... """ def inner(f): setDescription(f, d) return f return inner ## File creation, deletion hooks
[docs]def create_file(filename, contents=""): """ Hook for creating files before a test. Example usage:: @grader.test @grader.before_test(create_file('hello.txt', 'Hello world!')) @grader.after_test(delete_file('hello.txt')) def hook_test(m): with open('hello.txt') as file: txt = file.read() # ... """ import collections if isinstance(contents, collections.Iterable) and not isinstance(contents, str): contents = "\n".join(map(str, contents)) def _inner(info): with open(filename, "w") as f: f.write(contents) return _inner
[docs]def delete_file(filename): """ Hook for deleting files after a test. Example usage:: @grader.test @grader.before_test(create_file('hello.txt', 'Hello world!')) @grader.after_test(delete_file('hello.txt')) def hook_test(m): with open('hello.txt') as file: txt = file.read() # ... """ def _inner(result): try: os.remove(filename) except: pass return _inner
[docs]def create_temporary_file(filename, contents=""): """ Decorator for constructing a file which is available during a single test and is deleted afterwards. Example usage:: @grader.test @create_temporary_file('hello.txt', 'Hello world!') def hook_test(m): with open('hello.txt') as file: txt = file.read() """ from grader.core import before_test, after_test def _inner(test_function): before_test(create_file(filename, contents))(test_function) after_test(delete_file(filename))(test_function) return test_function return _inner
[docs]def add_value(value_name, value_or_fn): """ Post-test hook which as the value or the result of evaluating function on result to the test result dict. Example usage:: @test @after_test(add_value("grade", 7)) def graded_testcase(m): ... """ def _inner(result): value = value_or_fn if hasattr(value, '__call__'): value = value_or_fn(result) result[value_name] = value return _inner
@test_decorator
[docs]def expose_ast(test_function): """ Pre-test hook for exposing the ast of the solution module as an argument to the tester. Example usage:: @grader.test @grader.expose_ast def ast_test(m, AST): ... """ import ast from grader.core import before_test def _hook(info): code = read_code(info["solution_path"]) # add the solutions AST as a named argument to the test function info["extra_kwargs"]["AST"] = ast.parse(code) # add function _hook as a pre-hook to test_function return before_test(_hook)(test_function)