def clean_step_class(cls): assert cls.__name__ != "step_name_here" text = cls.text or cls.__doc__ program = cls.program hints = cls.hints solution = cls.__dict__.get("solution", "") assert bool(solution) ^ bool(program) assert text no_weird_whitespace(text) if solution: assert cls.tests program, cls.solution = clean_program(solution, cls) else: program, _ = clean_program(program, cls) assert program if isinstance(hints, str): hints = hints.strip().splitlines() hints = [highlighted_markdown(hint) for hint in hints] if "__program_" in text: text = text.replace("__program__", program) indented = indent(program, ' ') text = re.sub(r" *__program_indented__", indented, text, flags=re.MULTILINE) else: assert not cls.program_in_text, "Either include __program__ or __program_indented__ in the text, " \ "or set program_in_text = False in the class." assert "__program_" not in text text = highlighted_markdown(dedent(text).strip()) messages = [] for name, inner_cls in inspect.getmembers(cls): if not (isinstance(inner_cls, type) and issubclass(inner_cls, Step)): continue assert issubclass(inner_cls, MessageStep) inner_cls.tests = inner_cls.tests or cls.tests clean_step_class(inner_cls) # noinspection PyAbstractClass class inner_cls(inner_cls, cls): __name__ = inner_cls.__name__ __qualname__ = inner_cls.__qualname__ __module__ = inner_cls.__module__ messages.append(inner_cls) if inner_cls.after_success and issubclass(inner_cls, ExerciseStep): check_exercise( bind_self(inner_cls.solution), bind_self(cls.solution), cls.test_exercise, cls.generate_inputs, ) setattrs(cls, text=text, program=program, messages=messages, hints=hints) if hints: cls.get_solution = get_solution(cls) if cls.predicted_output_choices: cls.predicted_output_choices.append("Error") cls.predicted_output_choices = [ s.rstrip() for s in cls.predicted_output_choices ] if not cls.correct_output: cls.correct_output = get_stdout(cls.program).rstrip() assert cls.correct_output in cls.predicted_output_choices assert cls.correct_output != "Error" assert cls.correct_output if isinstance(cls.disallowed, Disallowed): cls.disallowed = [cls.disallowed]
def arg_names(cls): return list(inspect.signature(bind_self(cls.solution)).parameters)