refactor: do not require casex.in exists
This commit is contained in:
parent
d211d7a429
commit
45450b4451
|
@ -92,6 +92,7 @@ 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"):
|
||||||
|
|
|
@ -43,14 +43,17 @@ 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: Union[InputFile, StreamIn] = MemoryFile(content="")
|
stdin: Stdin = MemoryFile(content="")
|
||||||
stdout: Union[Collector, StreamOut] = Collector(name="stdout")
|
stdout: Stdout = Collector(name="stdout")
|
||||||
stderr: Union[Collector, StreamOut] = Collector(name="stderr")
|
stderr: Stderr = 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,
|
||||||
|
@ -77,9 +80,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[Union[InputFile, StreamIn]] = None
|
stdin: Optional[Stdin] = None
|
||||||
stdout: Optional[Union[Collector, StreamOut]] = None
|
stdout: Optional[Stdout] = None
|
||||||
stderr: Optional[Union[Collector, StreamOut]] = None
|
stderr: Optional[Stderr] = 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")
|
||||||
|
|
|
@ -130,8 +130,7 @@ class Parser(str, Enum):
|
||||||
ELF = "elf"
|
ELF = "elf"
|
||||||
|
|
||||||
|
|
||||||
class Stage(BaseModel):
|
class Case(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()
|
||||||
|
@ -140,9 +139,16 @@ class Stage(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")
|
||||||
)
|
)
|
||||||
score: int = 0
|
|
||||||
parsers: List[Parser] = [] # list of parsers
|
|
||||||
limit: Limit = Limit()
|
limit: Limit = Limit()
|
||||||
|
score: int = 0
|
||||||
|
diff: ParserDiff = ParserDiff()
|
||||||
|
|
||||||
|
|
||||||
|
class Stage(Case):
|
||||||
|
name: str = "" # stage name
|
||||||
|
skip: List[str] = []
|
||||||
|
|
||||||
|
parsers: List[Parser] = [] # list of parsers
|
||||||
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")
|
||||||
|
@ -157,11 +163,8 @@ class Stage(BaseModel):
|
||||||
validation_alias=AliasChoices("result-detail", "result_detail"),
|
validation_alias=AliasChoices("result-detail", "result_detail"),
|
||||||
)
|
)
|
||||||
file: ParserFile = ParserFile()
|
file: ParserFile = ParserFile()
|
||||||
skip: List[str] = []
|
|
||||||
|
|
||||||
# cases related
|
cases: Dict[str, Case] = {}
|
||||||
cases: Dict[str, "Stage"] = {}
|
|
||||||
diff: ParserDiff = ParserDiff()
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="allow")
|
model_config = ConfigDict(extra="allow")
|
||||||
|
|
||||||
|
|
|
@ -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, Set, Tuple
|
from typing import Any, Callable, Dict, List, Optional, 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,7 +11,6 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,18 +52,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[ParserEnum, Tuple[Callable[[Any, result.Parser], None], Any]]:
|
) -> Dict[task.Parser, Tuple[Callable[[Any, result.Parser], None], Any]]:
|
||||||
return {
|
return {
|
||||||
ParserEnum.ELF: (fix_keyword, task_stage.elf),
|
task.Parser.ELF: (fix_keyword, task_stage.elf),
|
||||||
ParserEnum.CLANG_TIDY: (fix_keyword, task_stage.clangtidy),
|
task.Parser.CLANG_TIDY: (fix_keyword, task_stage.clangtidy),
|
||||||
ParserEnum.KEYWORD: (fix_keyword, task_stage.keyword),
|
task.Parser.KEYWORD: (fix_keyword, task_stage.keyword),
|
||||||
ParserEnum.CPPCHECK: (fix_keyword, task_stage.cppcheck),
|
task.Parser.CPPCHECK: (fix_keyword, task_stage.cppcheck),
|
||||||
ParserEnum.CPPLINT: (fix_keyword, task_stage.cpplint),
|
task.Parser.CPPLINT: (fix_keyword, task_stage.cpplint),
|
||||||
ParserEnum.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
|
task.Parser.RESULT_DETAIL: (fix_result_detail, task_stage.result_detail),
|
||||||
ParserEnum.DUMMY: (fix_dummy, task_stage.dummy),
|
task.Parser.DUMMY: (fix_dummy, task_stage.dummy),
|
||||||
ParserEnum.RESULT_STATUS: (fix_dummy, task_stage.result_status),
|
task.Parser.RESULT_STATUS: (fix_dummy, task_stage.result_status),
|
||||||
ParserEnum.FILE: (fix_file, task_stage.file),
|
task.Parser.FILE: (fix_file, task_stage.file),
|
||||||
ParserEnum.DIFF: (
|
task.Parser.DIFF: (
|
||||||
partial(
|
partial(
|
||||||
fix_diff,
|
fix_diff,
|
||||||
task_stage=task_stage,
|
task_stage=task_stage,
|
||||||
|
@ -178,36 +177,31 @@ 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
|
||||||
# all intended testcases that is detected
|
# cases not specified in the toml config (auto-detected)
|
||||||
testcases = get_testcases(task_root, task_path)
|
unspecified_cases = get_unspecified_cases(task_root, task_path, task_stage.cases)
|
||||||
# all testcases that is not specified in the toml config
|
# cases specified in toml config but not skipped
|
||||||
default_cases = sorted(
|
specified_cases = [
|
||||||
testcases.difference(
|
(case, task_stage.cases[case])
|
||||||
[
|
for case in task_stage.cases
|
||||||
casei
|
if case not in task_stage.skip
|
||||||
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, case_stage in valid_cases:
|
for case_name, case in specified_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=result.LocalFile(
|
stdin=stdin,
|
||||||
src=str(base_dir / (case_stage.in_ or f"{case}.in"))
|
args=shlex.split(case.command) if case.command else None,
|
||||||
),
|
cpu_limit=case.limit.cpu,
|
||||||
args=shlex.split(case_stage.command) if case_stage.command else None,
|
clock_limit=DEFAULT_CLOCK_LIMIT_MULTIPLIER * case.limit.cpu,
|
||||||
cpu_limit=case_stage.limit.cpu,
|
memory_limit=case.limit.mem,
|
||||||
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:
|
||||||
|
@ -224,22 +218,22 @@ def fix_diff(
|
||||||
parser_case = result.DiffCasesConfig(
|
parser_case = result.DiffCasesConfig(
|
||||||
outputs=[
|
outputs=[
|
||||||
result.DiffOutputConfig(
|
result.DiffOutputConfig(
|
||||||
score=case_stage.diff.output.score,
|
score=case.diff.output.score,
|
||||||
file_name="stdout",
|
file_name="stdout",
|
||||||
answer_path=str(base_dir / (case_stage.out_ or f"{case}.out")),
|
answer_path=stdout,
|
||||||
force_quit_on_diff=case_stage.diff.output.force_quit,
|
force_quit_on_diff=case.diff.output.force_quit,
|
||||||
always_hide=case_stage.diff.output.hide,
|
always_hide=case.diff.output.hide,
|
||||||
compare_space=not case_stage.diff.output.ignore_spaces,
|
compare_space=not case.diff.output.ignore_spaces,
|
||||||
max_diff_length=case_stage.diff.output.max_length,
|
max_diff_length=case.diff.output.max_length,
|
||||||
max_diff_lines=case_stage.diff.output.max_lines,
|
max_diff_lines=case.diff.output.max_lines,
|
||||||
hide_common_prefix=case_stage.diff.output.hide_common_prefix,
|
hide_common_prefix=case.diff.output.hide_common_prefix,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
parser_cases.append(parser_case)
|
parser_cases.append(parser_case)
|
||||||
for case in default_cases:
|
for case_name in unspecified_cases:
|
||||||
cmd = result.OptionalCmd(
|
cmd = result.OptionalCmd(
|
||||||
stdin=result.LocalFile(src=str(base_dir / f"{case}.in")),
|
stdin=result.LocalFile(src=str(base_dir / f"{case_name}.in")),
|
||||||
cpu_limit=None,
|
cpu_limit=None,
|
||||||
clock_limit=None,
|
clock_limit=None,
|
||||||
memory_limit=None,
|
memory_limit=None,
|
||||||
|
@ -251,7 +245,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}.out"),
|
answer_path=str(base_dir / f"{case_name}.out"),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -260,9 +254,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_testcases(
|
def get_unspecified_cases(
|
||||||
task_root: Path, task_path: Path
|
task_root: Path, task_path: Path, cases: Dict[str, task.Case]
|
||||||
) -> Set[str]: # basedir here should be task_conf.root / task_conf.path
|
) -> List[str]:
|
||||||
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():
|
||||||
|
@ -279,4 +273,42 @@ def get_testcases(
|
||||||
)
|
)
|
||||||
).removesuffix(".in")
|
).removesuffix(".in")
|
||||||
)
|
)
|
||||||
return testcases
|
return sorted(
|
||||||
|
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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user