Compare commits

..

No commits in common. "45450b44516e3963081af749a732ba1573481873" and "f88c5b410640eb0eb070eec29ae5ef251a8ba0ba" have entirely different histories.

5 changed files with 70 additions and 111 deletions

View File

@ -92,7 +92,6 @@ def convert(
""" """
Convert given dir of JOJ3 toml config files to JOJ3 json config files Convert given dir of JOJ3 toml config files to JOJ3 json config files
""" """
app.pretty_exceptions_enable = False
logger.info(f"Converting files in {root.absolute()}") logger.info(f"Converting files in {root.absolute()}")
for repo_toml_path in root.glob("**/repo.toml"): for repo_toml_path in root.glob("**/repo.toml"):
for task_toml_path in repo_toml_path.parent.glob("**/*.toml"): for task_toml_path in repo_toml_path.parent.glob("**/*.toml"):

View File

@ -43,17 +43,14 @@ class StreamOut(BaseModel):
InputFile = Union[LocalFile, MemoryFile, PreparedFile, Symlink] InputFile = Union[LocalFile, MemoryFile, PreparedFile, Symlink]
Stdin = Union[InputFile, StreamIn]
Stdout = Union[Collector, StreamOut]
Stderr = Union[Collector, StreamOut]
class Cmd(BaseModel): class Cmd(BaseModel):
args: List[str] = [] args: List[str] = []
env: List[str] = [DEFAULT_PATH_ENV] env: List[str] = [DEFAULT_PATH_ENV]
stdin: Stdin = MemoryFile(content="") stdin: Union[InputFile, StreamIn] = MemoryFile(content="")
stdout: Stdout = Collector(name="stdout") stdout: Union[Collector, StreamOut] = Collector(name="stdout")
stderr: Stderr = Collector(name="stderr") stderr: Union[Collector, StreamOut] = Collector(name="stderr")
cpu_limit: int = Field(DEFAULT_CPU_LIMIT, serialization_alias="cpuLimit") cpu_limit: int = Field(DEFAULT_CPU_LIMIT, serialization_alias="cpuLimit")
clock_limit: int = Field( clock_limit: int = Field(
DEFAULT_CLOCK_LIMIT_MULTIPLIER * DEFAULT_CPU_LIMIT, DEFAULT_CLOCK_LIMIT_MULTIPLIER * DEFAULT_CPU_LIMIT,
@ -80,9 +77,9 @@ class Cmd(BaseModel):
class OptionalCmd(BaseModel): class OptionalCmd(BaseModel):
args: Optional[List[str]] = None args: Optional[List[str]] = None
env: Optional[List[str]] = None env: Optional[List[str]] = None
stdin: Optional[Stdin] = None stdin: Optional[Union[InputFile, StreamIn]] = None
stdout: Optional[Stdout] = None stdout: Optional[Union[Collector, StreamOut]] = None
stderr: Optional[Stderr] = None stderr: Optional[Union[Collector, StreamOut]] = None
cpu_limit: Optional[int] = Field(None, serialization_alias="cpuLimit") cpu_limit: Optional[int] = Field(None, serialization_alias="cpuLimit")
clock_limit: Optional[int] = Field(None, serialization_alias="clockLimit") clock_limit: Optional[int] = Field(None, serialization_alias="clockLimit")
memory_limit: Optional[int] = Field(None, serialization_alias="memoryLimit") memory_limit: Optional[int] = Field(None, serialization_alias="memoryLimit")

View File

@ -130,7 +130,8 @@ class Parser(str, Enum):
ELF = "elf" ELF = "elf"
class Case(BaseModel): class Stage(BaseModel):
name: str = "" # Stage name
env: List[str] = [] env: List[str] = []
command: str = "" # Command to run command: str = "" # Command to run
files: StageFiles = StageFiles() files: StageFiles = StageFiles()
@ -139,16 +140,9 @@ class Case(BaseModel):
copy_in_cwd: bool = Field( copy_in_cwd: bool = Field(
True, validation_alias=AliasChoices("copy-in-cwd", "copy_in_cwd") True, validation_alias=AliasChoices("copy-in-cwd", "copy_in_cwd")
) )
limit: Limit = Limit()
score: int = 0 score: int = 0
diff: ParserDiff = ParserDiff()
class Stage(Case):
name: str = "" # stage name
skip: List[str] = []
parsers: List[Parser] = [] # list of parsers parsers: List[Parser] = [] # list of parsers
limit: Limit = Limit()
dummy: ParserDummy = ParserDummy() dummy: ParserDummy = ParserDummy()
result_status: ParserDummy = Field( result_status: ParserDummy = Field(
ParserDummy(), validation_alias=AliasChoices("result-status", "result_status") ParserDummy(), validation_alias=AliasChoices("result-status", "result_status")
@ -163,8 +157,11 @@ class Stage(Case):
validation_alias=AliasChoices("result-detail", "result_detail"), validation_alias=AliasChoices("result-detail", "result_detail"),
) )
file: ParserFile = ParserFile() file: ParserFile = ParserFile()
skip: List[str] = []
cases: Dict[str, Case] = {} # cases related
cases: Dict[str, "Stage"] = {}
diff: ParserDiff = ParserDiff()
model_config = ConfigDict(extra="allow") model_config = ConfigDict(extra="allow")

View File

@ -2,7 +2,7 @@ import re
import shlex import shlex
from functools import partial from functools import partial
from pathlib import Path, PurePosixPath from pathlib import Path, PurePosixPath
from typing import Any, Callable, Dict, List, Optional, Tuple from typing import Any, Callable, Dict, List, Set, Tuple
from joj3_config_generator.models import result, task from joj3_config_generator.models import result, task
from joj3_config_generator.models.common import Memory, Time from joj3_config_generator.models.common import Memory, Time
@ -11,6 +11,7 @@ from joj3_config_generator.models.const import (
DEFAULT_PATH_ENV, DEFAULT_PATH_ENV,
JOJ3_CONFIG_ROOT, JOJ3_CONFIG_ROOT,
) )
from joj3_config_generator.models.task import Parser as ParserEnum
from joj3_config_generator.utils.logger import logger from joj3_config_generator.utils.logger import logger
@ -52,18 +53,18 @@ def get_parser_handler_map(
executor: result.Executor, executor: result.Executor,
task_root: Path, task_root: Path,
task_path: Path, task_path: Path,
) -> Dict[task.Parser, Tuple[Callable[[Any, result.Parser], None], Any]]: ) -> Dict[ParserEnum, Tuple[Callable[[Any, result.Parser], None], Any]]:
return { return {
task.Parser.ELF: (fix_keyword, task_stage.elf), ParserEnum.ELF: (fix_keyword, task_stage.elf),
task.Parser.CLANG_TIDY: (fix_keyword, task_stage.clangtidy), ParserEnum.CLANG_TIDY: (fix_keyword, task_stage.clangtidy),
task.Parser.KEYWORD: (fix_keyword, task_stage.keyword), ParserEnum.KEYWORD: (fix_keyword, task_stage.keyword),
task.Parser.CPPCHECK: (fix_keyword, task_stage.cppcheck), ParserEnum.CPPCHECK: (fix_keyword, task_stage.cppcheck),
task.Parser.CPPLINT: (fix_keyword, task_stage.cpplint), ParserEnum.CPPLINT: (fix_keyword, task_stage.cpplint),
task.Parser.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail), ParserEnum.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
task.Parser.DUMMY: (fix_dummy, task_stage.dummy), ParserEnum.DUMMY: (fix_dummy, task_stage.dummy),
task.Parser.RESULT_STATUS: (fix_dummy, task_stage.result_status), ParserEnum.RESULT_STATUS: (fix_dummy, task_stage.result_status),
task.Parser.FILE: (fix_file, task_stage.file), ParserEnum.FILE: (fix_file, task_stage.file),
task.Parser.DIFF: ( ParserEnum.DIFF: (
partial( partial(
fix_diff, fix_diff,
task_stage=task_stage, task_stage=task_stage,
@ -177,31 +178,36 @@ def fix_diff(
task_path: Path, task_path: Path,
) -> None: ) -> None:
base_dir = JOJ3_CONFIG_ROOT / task_path.parent base_dir = JOJ3_CONFIG_ROOT / task_path.parent
# cases not specified in the toml config (auto-detected) # all intended testcases that is detected
unspecified_cases = get_unspecified_cases(task_root, task_path, task_stage.cases) testcases = get_testcases(task_root, task_path)
# cases specified in toml config but not skipped # all testcases that is not specified in the toml config
specified_cases = [ default_cases = sorted(
(case, task_stage.cases[case]) testcases.difference(
for case in task_stage.cases [
if case not in task_stage.skip casei
for casei in testcases
if any(casei.endswith(casej) for casej in task_stage.cases)
]
)
)
# those in toml config that is not skipped
valid_cases = [
(casej, task_stage.cases[casei])
for casei in task_stage.cases
for casej in testcases
if (casei not in task_stage.skip and casej.endswith(casei))
] ]
stage_cases = [] stage_cases = []
parser_cases = [] parser_cases = []
for case_name, case in specified_cases: for case, case_stage in valid_cases:
stdin, stdout = get_stdin_stdout(task_root, task_path, case_name, case)
if stdout is None:
logger.warning(
f"In file {task_root / task_path}, "
f"testcase {case_name} has no corresponding .out file, "
"skipped"
)
continue
cmd = result.OptionalCmd( cmd = result.OptionalCmd(
stdin=stdin, stdin=result.LocalFile(
args=shlex.split(case.command) if case.command else None, src=str(base_dir / (case_stage.in_ or f"{case}.in"))
cpu_limit=case.limit.cpu, ),
clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case.limit.cpu, args=shlex.split(case_stage.command) if case_stage.command else None,
memory_limit=case.limit.mem, cpu_limit=case_stage.limit.cpu,
clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case_stage.limit.cpu,
memory_limit=case_stage.limit.mem,
proc_limit=task_stage.limit.proc, proc_limit=task_stage.limit.proc,
) )
if cmd.args == executor.with_.default.args: if cmd.args == executor.with_.default.args:
@ -218,22 +224,22 @@ def fix_diff(
parser_case = result.DiffCasesConfig( parser_case = result.DiffCasesConfig(
outputs=[ outputs=[
result.DiffOutputConfig( result.DiffOutputConfig(
score=case.diff.output.score, score=case_stage.diff.output.score,
file_name="stdout", file_name="stdout",
answer_path=stdout, answer_path=str(base_dir / (case_stage.out_ or f"{case}.out")),
force_quit_on_diff=case.diff.output.force_quit, force_quit_on_diff=case_stage.diff.output.force_quit,
always_hide=case.diff.output.hide, always_hide=case_stage.diff.output.hide,
compare_space=not case.diff.output.ignore_spaces, compare_space=not case_stage.diff.output.ignore_spaces,
max_diff_length=case.diff.output.max_length, max_diff_length=case_stage.diff.output.max_length,
max_diff_lines=case.diff.output.max_lines, max_diff_lines=case_stage.diff.output.max_lines,
hide_common_prefix=case.diff.output.hide_common_prefix, hide_common_prefix=case_stage.diff.output.hide_common_prefix,
) )
] ]
) )
parser_cases.append(parser_case) parser_cases.append(parser_case)
for case_name in unspecified_cases: for case in default_cases:
cmd = result.OptionalCmd( cmd = result.OptionalCmd(
stdin=result.LocalFile(src=str(base_dir / f"{case_name}.in")), stdin=result.LocalFile(src=str(base_dir / f"{case}.in")),
cpu_limit=None, cpu_limit=None,
clock_limit=None, clock_limit=None,
memory_limit=None, memory_limit=None,
@ -245,7 +251,7 @@ def fix_diff(
result.DiffOutputConfig( result.DiffOutputConfig(
score=task_stage.diff.default_score, score=task_stage.diff.default_score,
file_name="stdout", file_name="stdout",
answer_path=str(base_dir / f"{case_name}.out"), answer_path=str(base_dir / f"{case}.out"),
) )
] ]
) )
@ -254,9 +260,9 @@ def fix_diff(
diff_parser.with_ = result.DiffConfig(name="diff", cases=parser_cases) diff_parser.with_ = result.DiffConfig(name="diff", cases=parser_cases)
def get_unspecified_cases( def get_testcases(
task_root: Path, task_path: Path, cases: Dict[str, task.Case] task_root: Path, task_path: Path
) -> List[str]: ) -> Set[str]: # basedir here should be task_conf.root / task_conf.path
testcases = set() testcases = set()
for testcases_path in (task_root / task_path).parent.glob("**/*.in"): for testcases_path in (task_root / task_path).parent.glob("**/*.in"):
if not testcases_path.with_suffix(".out").exists(): if not testcases_path.with_suffix(".out").exists():
@ -273,42 +279,4 @@ def get_unspecified_cases(
) )
).removesuffix(".in") ).removesuffix(".in")
) )
return sorted( return testcases
testcases.difference(
[
casei
for casei in testcases
if any(casei.endswith(casej) for casej in cases)
]
)
)
def get_stdin_stdout(
task_root: Path, task_path: Path, case_name: str, case: task.Case
) -> Tuple[result.Stdin, Optional[str]]:
case_stdout_name = case.out_ if case.out_ else f"{case_name}.out"
stdin: result.Stdin = result.MemoryFile(content="")
stdout = None
for case_stdout_path in (task_root / task_path).parent.glob("**/*.out"):
if case_stdout_path.name != case_stdout_name:
continue
stdout = str(JOJ3_CONFIG_ROOT / case_stdout_path.relative_to(task_root))
case_stdin_path = case_stdout_path.with_suffix(".in")
if case.in_:
case_stdin_path = Path((task_root / task_path).parent / case.in_)
if not case_stdin_path.exists():
logger.warning(
f"In file {task_root / task_path}, "
f"testcase {case_stdout_path} has no .in file, "
"use empty content as stdin"
)
else:
stdin = result.LocalFile(
src=str(
JOJ3_CONFIG_ROOT
/ PurePosixPath(case_stdin_path.relative_to(task_root))
)
)
break
return stdin, stdout

View File

@ -94,7 +94,9 @@
}, },
{ {
"name": "debug", "name": "debug",
"with": {} "with": {
"score": 0
}
} }
] ]
}, },
@ -847,10 +849,6 @@
"with": { "with": {
"msg": "joj3 summary" "msg": "joj3 summary"
} }
},
{
"name": "debug",
"with": {}
} }
] ]
} }