+class QuestionCommandLine (_cmd.Cmd):
+ _help = [
+ 'Type help or ? to list commands.',
+ 'Non-commands will be interpreted as answers.',
+ 'Use a blank line to terminate multi-line answers.',
+ ]
+ intro = '\n'.join(['Welcome to the quizzer shell.'] + _help)
+ _prompt = 'quizzer? '
+
+ def __init__(self, ui):
+ super(QuestionCommandLine, self).__init__()
+ self.ui = ui
+ if self.ui.quiz.introduction:
+ self.intro = '\n\n'.join([self.intro, self.ui.quiz.introduction])
+ self._tempdir = None
+
+ def get_question(self):
+ self.question = self.ui.get_question()
+ if self.question:
+ self._reset()
+ else:
+ return True # out of questions
+
+ def preloop(self):
+ self.get_question()
+
+ def _reset(self):
+ self.answers = []
+ if self._tempdir:
+ self._tempdir.cleanup() # occasionally redundant, but that's ok
+ self._tempdir = None
+ self._set_ps1()
+
+ def _set_ps1(self):
+ "Pose a question and prompt"
+ if self.question:
+ self.prompt = '\n{}\n{}'.format(
+ _colorize(
+ self.ui.colors['question'], self.question.format_prompt()),
+ _colorize(self.ui.colors['prompt'], self._prompt))
+ else:
+ self.prompt = _colorize(self.ui.colors['prompt'], self._prompt)
+
+ def _set_ps2(self):
+ "Just prompt (without the question, e.g. for multi-line answers)"
+ self.prompt = _colorize(self.ui.colors['prompt'], self._prompt)
+
+ def default(self, line):
+ self.answers.append(line)
+ if self.question.multiline:
+ self._set_ps2()
+ else:
+ return self._answer()
+
+ def emptyline(self):
+ return self._answer()
+
+ def _answer(self):
+ if self.question.multiline:
+ answer = self.answers
+ elif self.answers:
+ answer = self.answers[0]
+ else:
+ answer = ''
+ kwargs = {}
+ if self._tempdir:
+ kwargs['tempdir'] = self._tempdir
+ correct = self.ui.process_answer(
+ question=self.question, answer=answer, **kwargs)
+ if correct:
+ print(_colorize(self.ui.colors['correct'], 'correct\n'))
+ else:
+ print(_colorize(self.ui.colors['incorrect'], 'incorrect\n'))
+ return self.get_question()
+
+ def do_answer(self, arg):
+ """Explicitly add a line to your answer
+
+ This is useful if the line you'd like to add starts with a
+ quizzer-shell command. For example:
+
+ quizzer? answer help=5
+ """
+ return self.default(arg)
+
+ def do_shell(self, arg):
+ """Run a shell command in the question temporary directory
+
+ For example, you can spawn an interactive session with:
+
+ quizzer? !bash
+
+ If the question does not allow interactive sessions, this
+ action is a no-op.
+ """
+ if getattr(self.question, 'allow_interactive', False):
+ if not self._tempdir:
+ self._tempdir = self.question.setup_tempdir()
+ try:
+ self._tempdir.invoke(
+ interpreter='/bin/sh', text=arg, stdout=None, stderr=None,
+ universal_newlines=False,
+ env=self.question.get_environment())
+ except (KeyboardInterrupt, _error.CommandError) as e:
+ if isinstance(e, KeyboardInterrupt):
+ LOG.warning('KeyboardInterrupt')
+ else:
+ LOG.warning(e)
+ self._tempdir.cleanup()
+ self._tempdir = None
+
+ def do_quit(self, arg):
+ "Stop taking the quiz"
+ self._reset()
+ return True
+
+ def do_skip(self, arg):
+ "Skip the current question, and continue with the quiz"
+ self.ui.stack.append(self.question)
+ return self.get_question()
+
+ def do_hint(self, arg):
+ "Show a hint for the current question"
+ self._reset()
+ print(self.question.format_help())
+
+ def do_copyright(self, arg):
+ "Print the quiz copyright notice"
+ if self.ui.quiz.copyright:
+ print('\n'.join(self.ui.quiz.copyright))
+ else:
+ print(self.ui.quiz.copyright)
+
+ def do_help(self, arg):
+ 'List available commands with "help" or detailed help with "help cmd"'
+ if not arg:
+ print('\n'.join(self._help))
+ super(QuestionCommandLine, self).do_help(arg)
+
+