Ejemplo n.º 1
0
 def compiler_output(self, namespace: str, stdout: str, stderr: str) \
         -> Tuple[List[Message], List[AnnotateCode], str, str]:
     stdout = self.cleanup_stacktrace(stdout,
                                      self.with_extension(namespace))
     if match := re.search(r".*: (.+Error): (.+) \(<code>, line (\d+)\)",
                           stdout):
         error = match.group(1)
         message = match.group(2)
         line = match.group(3)
         return [], [
             AnnotateCode(row=int(line),
                          text=f"{error}: {message}",
                          type=Severity.ERROR)
         ], stdout, stderr
Ejemplo n.º 2
0
class Python(Language):
    def get_string_quote(self):
        return '\''

    def compilation(self, bundle: Bundle, files: List[str]) -> CallbackResult:
        result = [x.replace(".py", ".pyc") for x in files]
        return [
            _executable(), "-W", "ignore", "-m", "compileall", "-q", "-b", "."
        ], result

    def execution(self, config: Config, cwd: Path, file: str,
                  arguments: List[str]) -> Command:
        return [_executable(), "-u", file, *arguments]

    def compiler_output(self, namespace: str, stdout: str, stderr: str) \
            -> Tuple[List[Message], List[AnnotateCode], str, str]:
        stdout = self.cleanup_stacktrace(stdout,
                                         self.with_extension(namespace))
        if match := re.search(r".*: (.+Error): (.+) \(<code>, line (\d+)\)",
                              stdout):
            error = match.group(1)
            message = match.group(2)
            line = match.group(3)
            return [], [
                AnnotateCode(row=int(line),
                             text=f"{error}: {message}",
                             type=Severity.ERROR)
            ], stdout, stderr
        elif stuff := self._attempt_stacktrace(stdout):
            line, column, message = stuff
            return [], [
                AnnotateCode(row=line,
                             column=column,
                             text=message,
                             type=Severity.ERROR)
            ], stdout, stderr
Ejemplo n.º 3
0
def run_eslint(bundle: Bundle, submission: Path, remaining: float) \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls eslint to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()
    if language_options.get("eslint_config", None):
        config_path = config.resources / language_options.get('eslint_config')
    else:
        # Use the default file.
        config_path = config.judge / "tested/languages/javascript/eslintrc.yml"
    config_path = config_path.absolute()

    execution_results = run_command(directory=submission.parent,
                                    timeout=remaining,
                                    command=[
                                        "eslint", "-f", "json",
                                        "--no-inline-config", "-c",
                                        config_path,
                                        submission.absolute()
                                    ])

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [
            get_i18n_string("languages.javascript.linter.timeout")
            if execution_results.timeout else
            get_i18n_string("languages.javascript.linter.memory")
        ], []

    try:
        eslint_objects = json.loads(execution_results.stdout)
    except Exception as e:
        logger.warning("ESLint produced bad output", exc_info=e)
        return [
            get_i18n_string("languages.javascript.linter.output"),
            ExtendedMessage(description=str(e),
                            format='code',
                            permission=Permission.STAFF)
        ], []
    annotations = []

    for eslint_object in eslint_objects:
        if Path(eslint_object.get('filePath',
                                  submission)).name != submission.name:
            continue
        for message in eslint_object.get('messages', []):
            text = message.get('message', None)
            if not text:
                continue
            rule_id = message.get('ruleId')
            if rule_id:
                text += f' (<a href="https://eslint.org/docs/rules/{rule_id}" ' \
                        f'target="_blank">{rule_id}</a>)'
            annotations.append(
                AnnotateCode(
                    row=max(int(message.get('line', "-1")) - 1, 0),
                    text=text,
                    column=max(int(message.get('column', "-1")) - 1, 0),
                    type=severity[int(message.get('severity', 1))],
                ))

    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations
Ejemplo n.º 4
0
def run_shellcheck(bundle: Bundle, submission: Path, remaining: float,
                   language: str = "bash") \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls shellcheck to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()
    if language_options.get("shellcheck_config", None):
        config_path = config.resources / language_options.get(
            'shellcheck_config')
        # Add shellcheck file in home folder
        shutil.copy2(Path(config_path), Path(Path.home(), ".shellcheckrc"))

    execution_results = run_command(directory=submission.parent,
                                    timeout=remaining,
                                    command=[
                                        "shellcheck", "-f", "json", "-s",
                                        language,
                                        submission.absolute()
                                    ])

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [
            get_i18n_string("languages.bash.linter.timeout")
            if execution_results.timeout else
            get_i18n_string("languages.bash.linter.memory")
        ], []

    try:
        shellcheck_objects = json.loads(execution_results.stdout)
    except Exception as e:
        logger.warning("ShellCheck produced bad output", exc_info=e)
        return [
            get_i18n_string("languages.bash.linter.output"),
            ExtendedMessage(description=str(e),
                            format='code',
                            permission=Permission.STAFF)
        ], []
    annotations = []

    for shellcheck_object in shellcheck_objects:
        if Path(shellcheck_object.get('file',
                                      submission)).name != submission.name:
            continue
        text = shellcheck_object.get('message', None)
        code = shellcheck_object.get('code', None)
        if not text and code is None:
            continue
        elif not text:
            text = f'(code <a href="https://github.com/koalaman/shellcheck/wiki/' \
                   f'SC{code}" target="_blank">{code}</a>)'
        elif code is not None:
            text = f'{text} (code <a href="https://github.com/koalaman/' \
                   f'shellcheck/wiki/SC{code}" target="_blank">{code}</a>)'
        annotations.append(
            AnnotateCode(
                row=max(int(shellcheck_object.get('line', "-1")) - 1, 0),
                text=text,
                column=max(int(shellcheck_object.get('column', "-1")) - 1, 0),
                type=message_categories.get(
                    shellcheck_object.get('level', "warning"),
                    Severity.WARNING),
            ))

    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations
Ejemplo n.º 5
0
def run_checkstyle(bundle: Bundle, submission: Path, remaining: float) \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls checkstyle to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()

    if ENV_CHECKSTYLE not in os.environ:
        return [
            ExtendedMessage(description=get_i18n_string(
                "languages.linter.not-found", linter="Checkstyle"),
                            format='text',
                            permission=Permission.STAFF)
        ], []

    checkstyle_jar = Path(os.environ[ENV_CHECKSTYLE])

    if language_options.get("checkstyle_config", None):
        config_path = config.resources / language_options.get(
            'checkstyle_config')
    else:
        # Use the default file.
        config_path = config.judge / "tested/languages/java/sun_tested_checks.xml"
    config_path = config_path.absolute()

    execution_results = run_command(directory=submission.parent,
                                    timeout=remaining,
                                    command=[
                                        "java", "-jar", checkstyle_jar, "-f",
                                        "xml", "-c", config_path,
                                        submission.name
                                    ])

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [
            get_i18n_string("languages.java.linter.timeout")
            if execution_results.timeout else
            get_i18n_string("languages.java.linter.memory")
        ], []

    try:
        xml_tree = ElementTree.fromstring(execution_results.stdout)
    except Exception as e:
        logger.warning("Checkstyle produced bad output", exc_info=e)
        return [
            get_i18n_string("languages.java.linter.output"),
            ExtendedMessage(description=str(e),
                            format='code',
                            permission=Permission.STAFF)
        ], []
    annotations = []

    for file_tree in xml_tree:
        if Path(file_tree.attrib.get('name',
                                     submission)).name != submission.name:
            continue
        for error_element in file_tree:
            message = error_element.attrib.get('message', None)
            if not message:
                continue
            source = error_element.attrib.get('source', None)
            if source:
                more_info = get_i18n_string("languages.linter.more-info")
                message += f' (https://checkstyle.sourceforge.io/apidocs/' \
                           f'index.html?{source.replace(".", "/")}.html" ' \
                           f'target="_blank">{more_info}</a>)'
            annotations.append(
                AnnotateCode(
                    row=max(
                        int(error_element.attrib.get('line', "-1")) - 1, 0),
                    text=message,
                    column=max(
                        int(error_element.attrib.get('column', "-1")) - 1, 0),
                    type=message_categories.get(
                        error_element.attrib.get('severity', "warning"),
                        Severity.WARNING),
                ))

    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations
Ejemplo n.º 6
0
def run_hlint(bundle: Bundle, submission: Path, remaining: float) \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls eslint to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()
    if language_options.get("hlint_config", None):
        config_path = config.resources / language_options.get('hlint_config')
    else:
        # Use the default file.
        config_path = config.judge / "tested/languages/haskell/hlint.yml"
    config_path = config_path.absolute()

    execution_results = run_command(directory=submission.parent,
                                    timeout=remaining,
                                    command=[
                                        "hlint", "-j", "--json", "-h",
                                        config_path,
                                        submission.absolute()
                                    ])

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [
            get_i18n_string("languages.haskell.linter.timeout")
            if execution_results.timeout else
            get_i18n_string("languages.haskell.linter.memory")
        ], []

    try:
        hlint_messages = json.loads(execution_results.stdout)
    except Exception as e:
        logger.warning("HLint produced bad output", exc_info=e)
        return [
            get_i18n_string("languages.haskell.linter.output"),
            ExtendedMessage(description=str(e),
                            format='code',
                            permission=Permission.STAFF)
        ], []
    annotations = []

    for hlint_message in hlint_messages:
        if Path(hlint_message.get('file', submission)).name != submission.name:
            continue
        notes = '\n'.join(hlint_message.get('note', []))
        hint = hlint_message.get('hint', None)
        if not hint:
            continue

        more_info = get_i18n_string("languages.linter.more-info")
        hint = f'{hint} <a href="https://github.com/ndmitchell/hlint/blob/master/' \
               f'hints.md" target="_blank">({more_info})</a>'

        hint_from = hlint_message.get('from', None)
        hint_to = hlint_message.get('to', None)
        if hint_from and hint_to:
            hint = f"{hint}\nfrom: `{hint_from}`\nto: `{hint_to}`"
        if notes:
            hint = f"{hint}\n{notes}"

        annotations.append(
            AnnotateCode(
                row=max(int(hlint_message.get('startLine', "-1")) - 1, 0),
                text=hint,
                column=max(int(hlint_message.get('startColumn', "-1")) - 1, 0),
                type=message_categories.get(
                    hlint_message.get('severity', "warning"),
                    Severity.WARNING),
            ))
    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations
Ejemplo n.º 7
0
def run_cppcheck(bundle: Bundle, submission: Path, remaining: float,
                 language: str = 'c') -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls cppcheck to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()

    execution_results = run_command(
        directory=submission.parent,
        timeout=remaining,
        command=["cppcheck", "--xml", "--enable=style,warning",
                 f"--language={language}", submission.name]
    )

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [get_i18n_string(
            "languages.c.linter.timeout") if execution_results.timeout else
                get_i18n_string("languages.c.linter.memory")], []

    try:
        xml_tree = ElementTree.fromstring(execution_results.stderr)
    except Exception as e:
        logger.warning("cppcheck produced bad output", exc_info=e)
        return [get_i18n_string("languages.c.linter.output"),
                ExtendedMessage(
                    description=str(e),
                    format='code',
                    permission=Permission.STAFF
                )], []
    annotations = []

    for element in xml_tree:
        if element.tag != 'errors':
            continue
        for error in element:
            message = error.attrib.get("verbose", None)
            if not message:
                continue
            severity = error.attrib.get("severity", "warning")
            position = None
            for el in error:
                if el.tag != 'location':
                    continue
                position = (max(int(el.attrib.get("line", "-1")) - 1, 0),
                            max(int(el.attrib.get("column", "-1")) - 1, 0))
                break
            annotations.append(AnnotateCode(
                row=position[0],
                text=message,
                column=position[1],
                type=message_categories.get(severity, Severity.WARNING)
            ))

    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations
Ejemplo n.º 8
0
def run_ktlint(bundle: Bundle, submission: Path, remaining: float) \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls ktlint to annotate submitted source code and adds resulting score and
    annotations to tab.
    """
    config = bundle.config
    language_options = bundle.config.config_for()

    if ENV_KTLINT not in os.environ:
        return [
            ExtendedMessage(description=get_i18n_string(
                "languages.linter.not-found", linter="KTLint"),
                            format='text',
                            permission=Permission.STAFF)
        ], []

    ktlint_jar = Path(os.environ[ENV_KTLINT])

    command = ["java", "-jar", ktlint_jar, "--reporter=json"]

    if language_options.get("editorconfig", None):
        command.append(
            "--editorconfig="
            f"{config.resources / language_options.get('editorconfig')}")

    if language_options.get("disabled_rules_ktlint", None):
        rules = language_options["disabled_rules_ktlint"]
        if isinstance(rules, list):
            rules = ",".join(rules)
        if "filename" not in rules:
            rules += ",filename"
        command.append(f"--disabled_rules={rules}")
    else:
        command.append("--disabled_rules=filename")

    if language_options.get("ktlint_ruleset", None):
        command.append(
            f"--ruleset={config.resources / language_options.get('ktlint_ruleset')}"
        )

    if language_options.get("ktlint_experimental", True):
        command.append("--experimental")

    submission = submission.absolute()

    command.append(submission.relative_to(submission.parent))

    execution_results = run_command(directory=submission.parent,
                                    timeout=remaining,
                                    command=command)

    if execution_results is None:
        return [], []

    if execution_results.timeout or execution_results.memory:
        return [
            get_i18n_string("languages.kotlin.linter.timeout")
            if execution_results.timeout else
            get_i18n_string("languages.kotlin.linter.memory")
        ], []

    try:
        ktlint_objects = json.loads(execution_results.stdout)
    except Exception as e:
        logger.warning("KTLint produced bad output", exc_info=e)
        return [
            get_i18n_string("languages.kotlin.linter.output"),
            ExtendedMessage(description=str(e),
                            format='code',
                            permission=Permission.STAFF)
        ], []
    annotations = []

    for ktlint_object in ktlint_objects:
        if Path(ktlint_object.get('file', submission)).name != submission.name:
            continue
        for error in ktlint_object.get('errors', []):
            message = error.get('message', None)
            if not message:
                continue
            rule = error.get('rule', None)
            if rule:
                more_info = get_i18n_string("languages.linter.more-info")
                message += f'({rule}, <a href="https://ktlint.github.io/#rules" ' \
                           f'target="_blank">{more_info}</a>)'

            annotations.append(
                AnnotateCode(
                    row=max(int(error.get('line', "-1")) - 1, 0),
                    text=message,
                    column=max(int(error.get('column', "-1")) - 1, 0),
                    type=Severity.INFO,
                ))

    # sort linting messages on line, column and code
    annotations.sort(key=lambda a: (a.row, a.column, a.text))
    return [], annotations