diff --git a/joj3_config_generator/models/result.py b/joj3_config_generator/models/result.py index 07e3f2b..7338c7a 100644 --- a/joj3_config_generator/models/result.py +++ b/joj3_config_generator/models/result.py @@ -1,6 +1,6 @@ -from typing import Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator from joj3_config_generator.models.const import ( DEFAULT_CPU_LIMIT, @@ -105,18 +105,34 @@ class OptionalCmd(BaseModel): class ExecutorWith(BaseModel): - default: Cmd - cases: List[OptionalCmd] + default: Cmd = Cmd() + cases: List[OptionalCmd] = [] class Executor(BaseModel): name: str - with_: ExecutorWith = Field(..., serialization_alias="with") + with_: ExecutorWith = Field(ExecutorWith(), serialization_alias="with") class Parser(BaseModel): name: str - with_: Dict[str, Any] = Field(..., serialization_alias="with") + if TYPE_CHECKING: + + class Empty(BaseModel): + pass + + with_: BaseModel = Field(Empty(), serialization_alias="with") + else: + with_: Dict[str, Any] = Field({}, serialization_alias="with") + + model_config = ConfigDict(validate_assignment=True) + + @field_validator("with_", mode="before") + @classmethod + def validate_with(cls, v: Any) -> Dict[str, Any]: + if isinstance(v, BaseModel): + return v.model_dump(by_alias=True) + raise ValueError(f"Must be a BaseModel instance") class StageDetail(BaseModel): @@ -200,3 +216,11 @@ class DiffCasesConfig(BaseModel): class DiffConfig(BaseModel): name: str = "diff" cases: List[DiffCasesConfig] = [] + + +class MsgConfig(BaseModel): + msg: str = "" + + +class ScoreConfig(BaseModel): + score: int = 0 diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py index 9b02636..5d9e87a 100644 --- a/joj3_config_generator/models/task.py +++ b/joj3_config_generator/models/task.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from pathlib import Path from typing import Any, Dict, List, Type -from pydantic import BaseModel, Field, field_validator, model_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from joj3_config_generator.models.common import Memory, Time from joj3_config_generator.models.const import ( @@ -63,6 +63,8 @@ class Limit(BaseModel): stderr: int = DEFAULT_FILE_LIMIT stdout: int = DEFAULT_FILE_LIMIT + model_config = ConfigDict(validate_assignment=True) + @field_validator("cpu", mode="before") @classmethod def ensure_time(cls, v: Any) -> Time: @@ -100,7 +102,7 @@ class Stage(BaseModel): cases: Dict[str, "Stage"] = {} diff: ParserDiff = ParserDiff() - model_config = {"extra": "allow"} + model_config = ConfigDict(extra="allow") @model_validator(mode="before") @classmethod diff --git a/joj3_config_generator/processers/repo.py b/joj3_config_generator/processers/repo.py index dce3fe4..f6320f9 100644 --- a/joj3_config_generator/processers/repo.py +++ b/joj3_config_generator/processers/repo.py @@ -29,7 +29,7 @@ def get_teapot_stage(repo_conf: repo.Config) -> result.StageDetail: cases=[], ), ), - parsers=[result.Parser(name="log", with_={"msg": "joj3 summary"})], + parsers=[result.Parser(name="log", with_=result.MsgConfig(msg="joj3 summary"))], ) return stage_conf @@ -87,8 +87,8 @@ def get_health_check_stage(repo_conf: repo.Config) -> result.StageDetail: ), ), parsers=[ - result.Parser(name="healthcheck", with_={"score": 1}), - result.Parser(name="debug", with_={"score": 0}), + result.Parser(name="healthcheck", with_=result.ScoreConfig(score=1)), + result.Parser(name="debug", with_=result.ScoreConfig(score=0)), ], ) return health_check_stage diff --git a/joj3_config_generator/processers/task.py b/joj3_config_generator/processers/task.py index 232bcec..84575f8 100644 --- a/joj3_config_generator/processers/task.py +++ b/joj3_config_generator/processers/task.py @@ -24,9 +24,7 @@ def get_conf_stage( name="sandbox", with_=get_executor_with(task_stage, cached), ), - parsers=( - [result.Parser(name=parser, with_={}) for parser in task_stage.parsers] - ), + parsers=([result.Parser(name=parser) for parser in task_stage.parsers]), ) processed_dict = get_processed_dict(task_stage) for idx, parser in enumerate(task_stage.parsers): @@ -108,11 +106,7 @@ def fix_keyword( else: continue - keyword_parser.with_.update( - result.KeywordMatchConfig( - matches=keyword_weight, - ).model_dump(by_alias=True) - ) + keyword_parser.with_ = result.KeywordMatchConfig(matches=keyword_weight) def fix_result_detail( @@ -124,15 +118,13 @@ def fix_result_detail( show_files.append("stdout") if result_detail_parser_config.stderr: show_files.append("stderr") - result_detail_parser.with_.update( - result.ResultDetailConfig( - score=0, - comment="", - show_files=show_files, - show_exit_status=result_detail_parser_config.exitstatus, - show_runtime=result_detail_parser_config.time, - show_memory=result_detail_parser_config.mem, - ).model_dump(by_alias=True) + result_detail_parser.with_ = result.ResultDetailConfig( + score=0, + comment="", + show_files=show_files, + show_exit_status=result_detail_parser_config.exitstatus, + show_runtime=result_detail_parser_config.time, + show_memory=result_detail_parser_config.mem, ) @@ -140,20 +132,15 @@ def fix_dummy( dummy_parser_config: task.ParserDummy, dummy_parser: result.Parser ) -> None: # we don't use dummy parser in real application - dummy_parser.with_.update( - result.DummyConfig( - score=dummy_parser_config.score, - comment=dummy_parser_config.comment, - force_quit_on_not_accepted=dummy_parser_config.force_quit, - ).model_dump(by_alias=True) + dummy_parser.with_ = result.DummyConfig( + score=dummy_parser_config.score, + comment=dummy_parser_config.comment, + force_quit_on_not_accepted=dummy_parser_config.force_quit, ) - return def fix_file(file_parser_config: task.ParserFile, file_parser: result.Parser) -> None: - file_parser.with_.update( - result.FileConfig(name=file_parser_config.name).model_dump(by_alias=True) - ) + file_parser.with_ = result.FileConfig(name=file_parser_config.name) def fix_diff( @@ -216,9 +203,5 @@ def fix_diff( ) ) - diff_parser_config.with_.update( - result.DiffConfig(name="diff", cases=parser_cases).model_dump(by_alias=True) - ) diff_executor.with_.cases = stage_cases - - return + diff_parser_config.with_ = result.DiffConfig(name="diff", cases=parser_cases)