def clean_program(program, cls): func = program if isinstance(program, FunctionType): source = dedent(inspect.getsource(program)) lines = source.splitlines() if lines[-1].strip().startswith("return "): func = NoMethodWrapper(program(None)) assert lines[0] == "def solution(self):" assert lines[-1] == f" return {func.__name__}" source = dedent("\n".join(lines[1:-1])) program = clean_solution_function(func, source) else: atok = ASTTokens(source, parse=True) func_node = atok.tree.body[0] lines = lines[func_node.body[0].first_token.start[0] - 1:] if hasattr(cls, "test_values"): inputs = list(cls.test_values())[0][0] else: inputs = {} inputs = inputs_string(inputs) program = inputs + '\n' + dedent('\n'.join(lines)) compile(program, "<program>", "exec") # check validity if not any(isinstance(node, ast.Return) for node in ast.walk(ast.parse(source))): func = returns_stdout(func) no_weird_whitespace(program) return program.strip(), func
def clean_program(program, inputs=None): if callable(program): inputs = inputs_string(inputs or {}) source = dedent(inspect.getsource(program)) atok = ASTTokens(source, parse=True) func = atok.tree.body[0] lines = source.splitlines()[func.body[0].first_token.start[0] - 1:] program = inputs + '\n' + dedent('\n'.join(lines)) compile(program, "<program>", "exec") # check validity no_weird_whitespace(program) return program.strip()
def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) if cls.__name__ == "Page": return pages[cls.slug] = cls page_slugs_list.append(cls.slug) cls.step_names = [] cls.step_texts = [] for key, value in cls.__dict__.items(): if getattr(value, "is_step", False): clean_step_class(value) cls.step_names.append(key) cls.step_texts.append(value.text) assert isinstance(cls.final_text, str) no_weird_whitespace(cls.final_text) cls.final_text = markdown(cls.final_text.strip()) cls.step_names.append("final_text") cls.step_texts.append(cls.final_text)
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 clean_step_class(cls, clean_inner=True): 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 # noinspection PyUnresolvedReferences inputs = list(cls.test_values())[0][0] program = clean_program(solution, inputs) else: program = clean_program(program) assert program if isinstance(hints, str): hints = hints.strip().splitlines() hints = [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 = markdown(dedent(text).strip()) messages = [] if clean_inner: for name, inner_cls in inspect.getmembers(cls): if not (isinstance(inner_cls, type) and issubclass(inner_cls, Step)): continue if 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__ program_in_text = inner_cls.program_in_text messages.append(inner_cls) if inner_cls.after_success and issubclass( inner_cls, ExerciseStep): check_exercise( partial(inner_cls.solution, None), partial(cls.solution, None), cls.test_exercise, cls.generate_inputs, ) clean_step_class(inner_cls, clean_inner=False) setattrs(cls, text=text, program=program, messages=messages, hints=hints)