Exemple #1
0
 def update_location(self, location):
     """ Updates both the fields and location attribute.
     TODO: Handle less information intelligently. """
     if isinstance(location, int):
         location = Location(location)
     self.location = location
     self.fields['location'] = location
Exemple #2
0
 def condition(self):
     """ Use find_matches to check number of occurrences. """
     name = self.fields['name']
     calls = find_function_calls(name, root=self.fields['root'])
     if calls:
         self.update_location(Location.from_ast(calls[-1]))
     return self._check_usage('call_count', calls)
Exemple #3
0
 def __init__(self, exception, context, traceback, location, **kwargs):
     report = kwargs.get('report', MAIN_REPORT)
     exception_name = get_exception_name(exception)
     exception_name_proper = add_indefinite_article(exception_name)
     exception_message = str(exception).capitalize()
     if type(exception) not in EXCEPTION_FF_MAP:
         title = exception_name
     else:
         title = EXCEPTION_FF_MAP[type(exception)].title
     location = Location(location)
     traceback_stack = traceback.build_traceback()
     traceback_message = traceback.format_traceback(traceback_stack,
                                                    report.format)
     context_message = format_contexts(context, report.format)
     fields = {'exception': exception,
               'exception_name': exception_name_proper,
               'exception_message': report.format.exception(exception_message),
               'location': location,
               'traceback': traceback,
               'traceback_stack': traceback_stack,
               'traceback_message': traceback_message,
               'context': context,
               'context_message': context_message}
     super().__init__(fields=fields, title=title,
                      location=location, **kwargs)
Exemple #4
0
 def __init__(self, line, filename, code, col_offset, exception, exc_info,
              **kwargs):
     report = kwargs.get('report', MAIN_REPORT)
     files = report.submission.get_files_lines()
     if filename not in files:
         files[filename] = code.split("\n")
     if report.submission is not None:
         lines = report.submission.get_lines()
         line_offsets = report.submission.line_offsets
     else:
         lines = code.split("\n")
         line_offsets = {}
     line_offset = line_offsets.get(filename, 0)
     traceback = ExpandedTraceback(exception, exc_info, False,
                                   [report.submission.instructor_file],
                                   line_offsets, [filename], lines, files)
     traceback_stack = traceback.build_traceback()
     traceback_message = traceback.format_traceback(traceback_stack,
                                                    report.format)
     line_offset = line_offsets.get(filename, 0)
     fields = {
         'lineno': line + line_offset,
         'filename': filename,
         'offset': col_offset,
         'exception': exception,
         'traceback': traceback,
         'traceback_stack': traceback_stack,
         'traceback_message': traceback_message
     }
     location = Location(line=line + line_offset,
                         col=col_offset,
                         filename=filename)
     super().__init__(fields=fields, location=location, **kwargs)
Exemple #5
0
 def condition(self):
     """ Use find_all to check number of occurrences. """
     name = self.fields['name']
     self.fields['name_message'] = AST_NODE_NAMES.get(name, name)
     uses = list(self.fields['root'].find_all(name))
     if uses:
         self.update_location(Location.from_ast(uses[-1]))
     return self._check_usage('use_count', uses)
Exemple #6
0
 def condition(self):
     """ Use find_matches to check number of occurrences. """
     name = self.fields['name']
     self.fields['name_message'] = self.report.format.python_expression(
         name)
     uses = list(find_operation(name, root=self.fields['root']))
     if uses:
         self.update_location(Location.from_ast(uses[-1]))
     return self._check_usage('use_count', uses)
Exemple #7
0
    def match_location(self, first=True):
        """
        Determines the first (or last) location of this match.

        Args:
            first (bool): Whether to use the first found match's location, or
                the last one.

        Returns:
            pedal.core.Location: The the first location of this match.
        """
        finder = min if first else max
        values = [
            v.lineno for v in self.mappings.values() if v.lineno is not None
        ]
        if not values:
            return Location(None)
        else:
            return Location(finder(values))
Exemple #8
0
    def locate(self, node: ast.AST = None):
        """
        Return a dictionary representing the current location within the
        AST.

        Returns:
            Location: The line and column of the current or given node.
        """
        if node is None:
            if self.node_chain:
                node = self.node_chain[-1]
            else:
                node = self.final_node
        return Location(node.lineno+self.line_offset, col=node.col_offset)
Exemple #9
0
def ensure_function(name,
                    arity=None,
                    parameters=None,
                    returns=None,
                    root=None,
                    compliment=False,
                    **kwargs):
    """ Checks that the function exists and has the right signature. """
    report = kwargs.get('report', MAIN_REPORT)
    root = root or parse_program(report=report)
    defs = root.find_all('FunctionDef')
    defs = [a_def for a_def in defs if a_def._name == name]
    if not defs:
        return missing_function(name, **kwargs)
    if len(defs) > 1:
        lines = [Location.from_ast(a_def) for a_def in defs]
        return duplicate_function_definition(name, lines, **kwargs)
    definition = defs[0]
    # Actual logic
    # 1.2.1 'arity' style - simply checks number of parameters
    if arity is not None or parameters is not None:
        expected_arity = arity if arity is not None else len(parameters)
        actual_arity = len(definition.args.args)
        if actual_arity < expected_arity:
            return too_few_parameters(name, actual_arity, expected_arity,
                                      **kwargs)
        elif actual_arity > expected_arity:
            return too_many_parameters(name, actual_arity, expected_arity,
                                       **kwargs)
    # 1.2.2 'parameters' style - checks each parameter's name and type
    if parameters is not None:
        actual_parameters = definition.args.args
        for expected_parameter, actual_parameter in zip(
                parameters, actual_parameters):
            expected_parameter_type = normalize_type(expected_parameter)
            actual_parameter_name = (actual_parameter.id if actual_parameter.id
                                     is not None else actual_parameter.arg)
            if actual_parameter.annotation is None:
                return missing_parameter_type(name, actual_parameter_name,
                                              expected_parameter_type,
                                              **kwargs)
            try:
                actual_parameter_type = normalize_type(
                    actual_parameter.annotation.ast_node)
            except ValueError as e:
                return invalid_parameter_type(name, actual_parameter_name,
                                              actual_parameter.annotation,
                                              expected_parameter_type,
                                              **kwargs)
            if not are_types_equal(actual_parameter_type,
                                   expected_parameter_type):
                return wrong_parameter_type(name, actual_parameter_name,
                                            actual_parameter_type,
                                            expected_parameter_type, **kwargs)
    # 1.2.3. 'returns' style - checks the return type explicitly
    if returns is not None:
        expected_returns = normalize_type(returns)
        if definition.returns is None:
            return missing_return_type(name, expected_returns, **kwargs)
        try:
            actual_returns = normalize_type(definition.returns.ast_node)
        except ValueError as e:
            return invalid_return_type(name, definition.returns,
                                       expected_returns, **kwargs)
        if not are_types_equal(actual_returns, expected_returns):
            return wrong_return_type(name, actual_returns, expected_returns,
                                     **kwargs)
    # Alternatively, returns positive FF?
    if compliment:
        if isinstance(compliment, str):
            core_compliment(compliment, label="function_defined", **kwargs)
        elif compliment is True:
            core_compliment(f"Defined {name}",
                            label="function_defined",
                            **kwargs)
    elif kwargs.get("score"):
        give_partial(kwargs.pop("score"), label="function_defined", **kwargs)

    return None
Exemple #10
0
    def __init__(self,
                 *args,
                 label=None,
                 category=None,
                 justification=None,
                 fields=None,
                 field_names=None,
                 kind=None,
                 title=None,
                 message=None,
                 message_template=None,
                 else_message=None,
                 priority=None,
                 valence=None,
                 location=None,
                 score=None,
                 correct=None,
                 muted=None,
                 unscored=None,
                 tool=None,
                 version=None,
                 author=None,
                 tags=None,
                 parent=None,
                 report=MAIN_REPORT,
                 delay_condition=False,
                 activate=True,
                 **kwargs):
        # Internal data
        self.report = report
        # Metadata
        if label is not None:
            self.label = label
        else:
            self.label = self.__class__.__name__
        # Condition
        if category is not None:
            self.category = category
        if justification is not None:
            self.justification = justification
        self.activate = activate
        # Response
        if kind is not None:
            self.kind = kind
        if priority is not None:
            self.priority = priority
        if valence is not None:
            self.valence = valence

        # Presentation
        if fields is not None:
            self.fields = fields
        else:
            self.fields = {}
        if self.constant_fields is not None:
            self.fields.update(self.constant_fields)
        if field_names is not None:
            self.field_names = field_names
        if title is not None:
            self.title = title
        elif self.title is None:
            self.title = label
        if message is not None:
            self.message = message
        if message_template is not None:
            self.message_template = message_template
        if else_message is not None:
            self.else_message = else_message

        # Locations
        if isinstance(location, int):
            location = Location(location)
        # TODO: Handle tuples (Line, Col) and (Filename, Line, Col), and
        #  possibly lists thereof
        if location is not None:
            self.location = location
        # Result
        if score is not None:
            self.score = score
        if correct is not None:
            self.correct = correct
        if muted is not None:
            self.muted = muted
        if unscored is not None:
            self.unscored = unscored
        # Metadata
        if tool is not None:
            self.tool = tool
        if version is not None:
            self.version = version
        if author is not None:
            self.author = author
        if tags is not None:
            self.tags = tags
        # Organizational
        if parent is not None:
            self.parent = parent
        if self.parent is None:
            # Might inherit from Report's current group
            self.parent = self.report.get_current_group()
        if self.field_names is not None:
            for field_name in self.field_names:
                self.fields[field_name] = kwargs.get(field_name)
        for key, value in kwargs.items():
            self.fields[key] = value
        if 'location' not in self.fields and self.location is not None:
            self.fields['location'] = self.location
        # Potentially delay the condition check
        self._stored_args = args
        self._stored_kwargs = kwargs
        if delay_condition:
            self._met_condition = False
            self._status = FeedbackStatus.DELAYED
        else:
            self._handle_condition()
Exemple #11
0
 def locate(self):
     """
     Returns:
         pedal.core.Location: The location of this node.
     """
     return Location(self.astNode.lineno, col=self.astNode.col_offset)