-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathdecorators.py
More file actions
170 lines (118 loc) · 4.64 KB
/
Copy pathdecorators.py
File metadata and controls
170 lines (118 loc) · 4.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
from functools import wraps, update_wrapper
import signal
class _update_wrapper_after_call(object):
"""Context manager to update a wrapper function after the wrapped function is called. Thus,
if the wrapped function modifies the wrapper state (as in @partial_credit, for example), any
changes to the wrapper will be preserved.
"""
def __init__(self, wrapper, func):
self.wrapper = wrapper
self.func = func
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
update_wrapper(self.wrapper, self.func)
class weight(object):
"""Simple decorator to add a __weight__ property to a function
Usage: @weight(3.0)
"""
def __init__(self, val):
self.val = val
def __call__(self, func):
func.__weight__ = self.val
return func
class number(object):
"""Simple decorator to add a __number__ property to a function
Usage: @number("1.1")
This field will then be used to sort the test results on Gradescope.
"""
def __init__(self, val):
self.val = str(val)
def __call__(self, func):
func.__number__ = self.val
return func
class visibility(object):
"""Simple decorator to add a __visibility__ property to a function
Usage: @visibility("hidden")
Options for the visibility field are as follows:
- `hidden`: test case will never be shown to students
- `after_due_date`: test case will be shown after the assignment's due date has passed.
If late submission is allowed, then test will be shown only after the late due date.
- `after_published`: test case will be shown only when the assignment is explicitly published from the "Review Grades" page
- `visible` (default): test case will always be shown
"""
def __init__(self, val):
self.val = val
def __call__(self, func):
func.__visibility__ = self.val
return func
class hide_errors(object):
"""Simple decorator to add a __hide_errors__ property to a function
Usage: @hide_errors("Error message to be shown upon test failure")
Used to hide the particular source of an error which caused a test to fail.
Otherwise, a test's particular assertions can be seen by students.
"""
def __init__(self, val="Test failed"):
self.val = val
def __call__(self, func):
func.__hide_errors__ = self.val
return func
class tags(object):
"""Simple decorator to add a __tags__ property to a function
Usage: @tags("concept1", "concept2")
"""
def __init__(self, *args):
self.tags = args
def __call__(self, func):
func.__tags__ = self.tags
return func
class leaderboard(object):
"""Decorator that indicates that a test corresponds to a leaderboard column
Usage: @leaderboard("high_score"). The string parameter indicates
the name of the column on the leaderboard
Then, within the test, set the value by calling
kwargs['set_leaderboard_value'] with a value. You can make this convenient by
explicitly declaring a set_leaderboard_value keyword argument, eg.
```
def test_highscore(set_leaderboard_value=None):
set_leaderboard_value(42)
```
"""
def __init__(self, column_name, sort_order='desc'):
self.column_name = column_name
self.sort_order = sort_order
def __call__(self, func):
func.__leaderboard_column__ = self.column_name
func.__leaderboard_sort_order__ = self.sort_order
def set_leaderboard_value(x):
wrapper.__leaderboard_value__ = x
@wraps(func)
def wrapper(*args, **kwargs):
kwargs['set_leaderboard_value'] = set_leaderboard_value
with _update_wrapper_after_call(wrapper, func):
return func(*args, **kwargs)
return wrapper
class partial_credit(object):
"""Decorator that indicates that a test allows partial credit
Usage: @partial_credit(test_weight)
Then, within the test, set the value by calling
kwargs['set_score'] with a value. You can make this convenient by
explicitly declaring a set_score keyword argument, eg.
```
@partial_credit(10)
def test_partial(set_score=None):
set_score(4.2)
```
"""
def __init__(self, weight):
self.weight = weight
def __call__(self, func):
func.__weight__ = self.weight
def set_score(x):
wrapper.__score__ = x
@wraps(func)
def wrapper(*args, **kwargs):
kwargs['set_score'] = set_score
with _update_wrapper_after_call(wrapper, func):
return func(*args, **kwargs)
return wrapper