Ejemplo n.º 1
0
 def __init__(self,
              files=None,
              main_file='answer.py',
              main_code=None,
              user=None,
              assignment=None,
              course=None,
              execution=None,
              instructor_file='on_run.py',
              skip_tifa=False,
              set_success=True,
              report=MAIN_REPORT):
     # Possibly user passed in stuff via the command line.
     if files is None and main_code is None:
         (instructor_file, files, main_file, main_code, user, assignment,
          course, execution) = parse_argv()
     super().__init__(files=files,
                      main_file=main_file,
                      main_code=main_code,
                      user=user,
                      assignment=assignment,
                      course=course,
                      execution=execution,
                      instructor_file=instructor_file,
                      report=report)
     # Then default custom stuff
     verify(report=report)
     self.ast = parse_program(report=report)
     if skip_tifa:
         self.tifa = None
     else:
         from pedal.tifa import tifa_analysis
         self.tifa = tifa_analysis(report=report)
     self.student = run(threaded=True, report=report)
     self.set_success = set_success
Ejemplo n.º 2
0
def find_operation(op_name, root=None, report=MAIN_REPORT):
    """
    Returns all the occurrences of the operator `op_name` in the source code.
    You can specify the operator as a string like `"+"` or `"<<"`.
    Supports all comparison, boolean, binary, and unary operators.
    """
    root = root or parse_program(report=report)
    found = []
    if op_name in COMPARE_OP_NAMES:
        compares = root.find_all("Compare")
        for compare in compares:
            for op in compare.ops:
                if op.ast_name == COMPARE_OP_NAMES[op_name]:
                    found.append(compare)
    elif op_name in BOOL_OP_NAMES:
        boolops = root.find_all("BoolOp")
        for boolop in boolops:
            if boolop.op_name == BOOL_OP_NAMES[op_name]:
                found.append(boolop)
    elif op_name in BIN_OP_NAMES:
        binops = root.find_all("BinOp")
        for binop in binops:
            if binop.op_name == BIN_OP_NAMES[op_name]:
                found.append(binop)
    elif op_name in UNARY_OP_NAMES:
        unaryops = root.find_all("UnaryOp")
        for unaryop in unaryops:
            if unaryop.op_name == UNARY_OP_NAMES[op_name]:
                found.append(unaryop)
    return found
Ejemplo n.º 3
0
def ensure_assignment(variable_name,
                      type=None,
                      value=None,
                      root=None,
                      muted=False,
                      report=MAIN_REPORT):
    """

    Consumes a variable name
    TODO: Implement the value parameter

    :param variable_name: The variable name the student is expected to define.
    :type variable_name: str
    :param type: The string type of the node on the right side of the
                 assignment. Check GreenTreeSnakes (e.g., "Num", or "Str").
    :type type: str
    :return: False or str

    Args:
        root:
        value:
    """
    if root is None:
        root = parse_program()
    # TODO: Tie in Tifa's custom types
    expected_type = get_tifa_type_from_str(type)
    # Find assignments matching the pattern
    pattern = "{variable_name} = __expr__".format(variable_name=variable_name)
    matches = root.find_matches(pattern)
    potentials = []
    for match in matches:
        if type is None:
            return match
        assigned_type = get_tifa_type_from_ast(match["__expr__"].ast_node)
        if are_types_equal(assigned_type, expected_type):
            return match
        potentials.append(match)
    if all(is_literal(potential) for potential in potentials):
        explain(
            ("You needed to assign a literal value to {variable}, but you "
             "created an expression instead.").format(variable=variable_name),
            label="did_not_assign_literal",
            title="Expression Instead of Literal",
            report=report,
            muted=muted)
    elif type is None:
        explain(("You have not properly assigned anything to the variable "
                 "{variable}.").format(variable=variable_name),
                label="no_assign",
                title="No Proper Assignment",
                report=report,
                muted=muted)
    else:
        explain(("You have not assigned a {type} to the variable {variable}."
                 "").format(type=type, variable=variable_name),
                label="wrong_type_assign",
                title="Unexpected Variable Type",
                report=report,
                muted=muted)
    return False
def grade_records():
    body = parse_program().body
    for statement in body:
        if statement.ast_name == "Expr":
            if statement.value.ast_name == "Str":
                contents = statement.value.s
                for line in contents.split("\n"):
                    if line.strip().lower().startswith("records:"):
                        give_partial(FUNCTION_VALUE)
                        return True
    explain("You have not created a Records definition at the top level.")
    return False
Ejemplo n.º 5
0
def find_seed(python_code):
    try:
        ast = parse_program(python_code)
        for assign in ast.find_all("Assign"):
            if assign.targets[0].ast_name != "Name":
                continue
            if assign.targets[0].id == "__STUDENT_SEED__":
                if assign.value.ast_name == "Str":
                    return assign.value.s
                elif assign.value.ast_name == "Num":
                    return assign.value.n
                elif assign.value.ast_name == "List":
                    return [e.n for e in assign.value.elts]
    except SyntaxError:
        return 0
    return 0
Ejemplo n.º 6
0
def find_function_definition(name, root=None, report=MAIN_REPORT):
    """
    Finds the given function definition based on the given ``name``.

    Args:
        name (str): The name of the function.
        root: A subtree of a parse tree to revert to.
        report (Report): The name of the Report to refer to.

    Returns:
        :py:class:`pedal.cait.cait_node.CaitNode`: The first occurrence of a
            function with the given name.
    """
    root = root or parse_program(report=report)
    defs = root.find_all('FunctionDef')
    for a_def in defs:
        if a_def._name == name:
            return a_def
    return None
Ejemplo n.º 7
0
def ensure_functions_return(exceptions=None, report=MAIN_REPORT, **kwargs):
    if exceptions is None:
        exceptions = set()
    elif isinstance(exceptions, str):
        exceptions = {exceptions}
    root = parse_program(report=report)
    defs = root.find_all("FunctionDef")
    for adef in defs:
        name = adef._name
        if name in exceptions:
            continue
        if not adef.find_match("return"):
            return explain(
                f"The function {name} is not returning."
                f" However, that function is supposed to have a return statement.",
                label="function_does_not_return",
                title="Must Return in Function",
                fields={'name': name})
    return False
Ejemplo n.º 8
0
def find_prior_initializations(node, report=MAIN_REPORT):
    """
    DEPRECATED

    Given a Name node, returns a list of all the assignment
    statements that incorporate that Name node prior to that line. Returns
    None if no Name is given.

    """
    if node.ast_name != "Name":
        return None
    ast = parse_program(report=report)
    assignments = ast.find_all("Assign")
    cur_line_no = node.lineno
    all_assignments = []
    for assignment in assignments:
        if assignment.has(node):
            if assignment.lineno < cur_line_no:
                all_assignments.append(assignment)
    return all_assignments
Ejemplo n.º 9
0
def is_top_level(ast_node, report=MAIN_REPORT) -> bool:
    """
    Determines if the `ast_node` is at the top-level of the program.
    Correctly handles expression statements (so a print call on its own will be
    considered a statement, even though its technically an expression).

    Args:
        ast_node (pedal.cait.cait_node.CaitNode): The CaitNode to check
        report (pedal.core.report.Report):

    Returns:
        bool: Whether the node is from the top level
    """
    ast = parse_program(report=report)
    for element in ast.body:
        if element.ast_name == "Expr":
            if element.value == ast_node:
                return True
        elif element == ast_node:
            return True
    return False
Ejemplo n.º 10
0
def prevent_printing_functions(exceptions=None, report=MAIN_REPORT, **kwargs):
    if exceptions is None:
        exceptions = set()
    elif isinstance(exceptions, str):
        exceptions = {exceptions}
    root = parse_program(report=report)
    defs = root.find_all("FunctionDef")
    for adef in defs:
        name = adef._name
        if name in exceptions:
            continue
        for call in find_function_calls("print", root=adef, report=report):
            location = call.locate()
            return explain(
                f"The function {name} is printing on line {location.line}."
                f" However, that function is not supposed to print.",
                label="function_is_printing",
                title="Do Not Print in Function",
                fields={'name': name},
                location=location,
                priority='syntax')
    return False
Ejemplo n.º 11
0
def find_function_calls(name: str, root=None, report=MAIN_REPORT):
    """
    Returns a list of CaitNodes representing all of the function calls that
    were found. This includes both methods and regular functions.

    Args:
        name (str): The name of the function to search.
        root: A subtree of a parse tree to revert to.
        report (Report): The name of the Report to refer to.

    Returns:
        List[CaitNode]: Relevant call nodes.
    """
    root = root or parse_program(report=report)
    all_calls = root.find_all("Call")
    calls = []
    for a_call in all_calls:
        if a_call.func.ast_name == "Attribute":
            if a_call.func.attr == name:
                calls.append(a_call)
        elif a_call.func.ast_name == "Name":
            if a_call.func.id == name:
                calls.append(a_call)
    return calls
Ejemplo n.º 12
0
    
import pedal
click("Imported pedal")

from pedal.source import set_source
click("Imported source")

set_source("a = 0")
click("Set source")

from pedal.tifa import tifa_analysis
click("Imported Tifa")

tifa_analysis()
click("Ran Tifa")

from pedal.cait import parse_program
click("Imported cait")

ast = parse_program()
click("Parsed program")

if ast.find_all("Assign"):
    print(ast.find_all("Assign"))
click("Found assignments")

from pedal.resolvers import simple
click("Imported resolver")

print(simple.resolve())
click("Resolved")
Ejemplo n.º 13
0
 def __init__(self, root=None, **kwargs):
     fields = kwargs.setdefault('fields', {})
     report = kwargs.setdefault('report', MAIN_REPORT)
     fields['root'] = root or parse_program(report=report)
     super().__init__(**kwargs)
Ejemplo n.º 14
0
set_source("a = 0")
click("Set source")

from pedal.tifa import tifa_analysis

click("Imported Tifa")

tifa_analysis()
click("Ran Tifa")

from pedal.cait import parse_program

click("Imported cait")

ast = parse_program()
click("Parsed program")

if ast.find_all("Assign"):
    print(ast.find_all("Assign"))
click("Found assignments")

from pedal.sandbox.sandbox import run

student = run()
print(student)
click("Ran sandbox")

from pedal.resolvers import simple

click("Imported resolver")
Ejemplo n.º 15
0
 def test_has_loop(question):
     ast = parse_program()
     if not ast.find_all("For"):
         gently("No for loop yet.")
     else:
         question.answer()