Beispiel #1
0
def pretty_print_timings(is_parallel: bool = False):
    global start_time
    lines = []
    if is_parallel:
        lines.append(f'_{get_i18n_string("timings.parallel")}_')
    for stage in calculate_stages():
        stage_name = get_i18n_string(f"timings.{stage.stage_name}")
        lines.append("{:s}: **{:0.03f} s**".format(stage_name, stage.duration))
        for sub_stage in stage.sub_stages:
            sub_stage_name = get_i18n_string(
                f"timings.{sub_stage.sub_stage_name}")
            lines.append("- {:s}: **{:0.03f} s**".format(
                sub_stage_name, sub_stage.duration))
    end_time = monotonic()
    lines.extend([
        "", "{:s}: _**{:0.03f} s**_".format(get_i18n_string(f"timings.total"),
                                            end_time - start_time)
    ])
    return '\n\n'.join(lines)
Beispiel #2
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
Beispiel #3
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
Beispiel #4
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
Beispiel #5
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
Beispiel #6
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
Beispiel #7
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
Beispiel #8
0
def run_pylint(bundle: Bundle, submission: Path, remaining: float) \
        -> Tuple[List[Message], List[AnnotateCode]]:
    """
    Calls pylint 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("pylint_config", None):
        config_path = config.resources / language_options.get('pylint_config')
    else:
        # Use the default file.
        config_path = config.judge / "tested/languages/python/pylint_config.rc"

    pylint_out = StringIO()
    try:
        args = [f"--rcfile={config_path}", str(submission)]
        logger.debug("Running with template_args %s", args)
        lint.Run(args, reporter=JSONReporter(output=pylint_out), do_exit=False)
    except Exception as e:
        logger.warning("Pylint crashed with", exc_info=e)
        return [get_i18n_string("languages.python.linter.crashed"), ExtendedMessage(
            description=str(e),
            format='code',
            permission=Permission.STAFF
        )], []

    try:
        messages = json.loads(pylint_out.getvalue())
    except Exception as e:
        logger.warning("Pylint produced bad output", exc_info=e)
        return [get_i18n_string("languages.python.linter.output"), ExtendedMessage(
            description=str(e),
            format='code',
            permission=Permission.STAFF
        )], []

    annotations = []

    for message in messages:
        category = message_categories.get(message.get("type", "warning"),
                                          Severity.WARNING)
        logger.debug("Handling message %s", str(message))
        message_id = message.get('message-id', None)
        message_text = message.get('message', None)
        more_info = get_i18n_string("languages.linter.more-info")
        if not message_id and not message_text:
            continue
        elif not message_id:
            text = message_text
        elif not message_text:
            text = f'({message_id}, <a href="https://pylint.pycqa.org/en/latest/' \
                   f'technical_reference/features.html#basic-checker-messages" ' \
                   f'target="_blank">{more_info}</a>)'
        else:
            text = f'{message_text} ({message_id},' \
                   f'<a href="https://pylint.pycqa.org/en/latest/' \
                   f'technical_reference/features.html#basic-checker-messages" ' \
                   f'target="_blank">{more_info}</a>)'
        annotations.append(AnnotateCode(
            row=max(int(message.get("line", "-1")) - 1, 0),
            column=max(int(message.get("column", "-1")) - 1, 0),
            text=text,
            type=category
        ))

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