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)
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
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
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
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
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
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
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