4 def register_question(question_class):
5 QUESTION_CLASS[question_class.__name__] = question_class
8 class Question (object):
17 def __init__(self, **kwargs):
18 self.__setstate__(kwargs)
21 return '<{} id:{!r}>'.format(type(self).__name__, self.id)
24 return '<{} id:{!r} at {:#x}>'.format(
25 type(self).__name__, self.id, id(self))
27 def __getstate__(self):
28 return {attr: getattr(self, attr)
29 for attr in self._state_attributes}
31 def __setstate__(self, state):
33 state['id'] = state.get('prompt', None)
34 if 'dependencies' not in state:
35 state['dependencies'] = []
36 for attr in self._state_attributes:
39 self.__dict__.update(state)
41 def check(self, answer):
42 return answer == self.answer
45 class NormalizedStringQuestion (Question):
46 def normalize(self, string):
47 return string.strip().lower()
49 def check(self, answer):
50 return self.normalize(answer) == self.normalize(self.answer)
53 class ScriptQuestion (Question):
54 _state_attributes = Question._state_attributes + [
60 def __setstate__(self, state):
61 if 'interpreter' not in state:
62 state['interpreter'] = 'sh' # POSIX-compatible shell
63 for key in ['setup', 'teardown']:
66 super(ScriptQuestion, self).__setstate__(state)
68 def check(self, answer):
69 script = '\n'.join(self.setup + [answer] + self.teardown)
70 raise ValueError(script)
73 for name,obj in list(locals().items()):
74 if name.startswith('_'):
77 subclass = issubclass(obj, Question)
78 except TypeError: # obj is not a class
81 register_question(obj)