Skip to content

Commit 9ccab88

Browse files
Improve compile command (kyuridenamida#178)
* Refactor compile command logic * Fix typos in setter * Refactor setter * Fix bug * Show identifier names instead of display names on language error * Refactor tester command fixing how to handle compile_only_when_diff_detected option * change setter command description * change command order
1 parent c8f0be8 commit 9ccab88

File tree

9 files changed

+209
-164
lines changed

9 files changed

+209
-164
lines changed

atcodertools/atcoder_tools.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,18 @@ def main():
4343
if len(sys.argv) < 2 or sys.argv[1] not in ("gen", "test", "submit", "codegen", "set", "version", "compile"):
4444
print("Usage:")
4545
print("{} gen -- to generate workspace".format(sys.argv[0]))
46-
print(
47-
"{} codegen -- to generate code of the specified single problem".format(sys.argv[0]))
4846
print(
4947
"{} test -- to test your code in the workspace".format(sys.argv[0]))
5048
print(
5149
"{} submit -- to submit your code to the contest system".format(sys.argv[0]))
5250
print(
53-
"{} version -- show atcoder-tools version".format(sys.argv[0]))
51+
"{} compile -- compile source code".format(sys.argv[0]))
5452
print(
55-
"{} set -- set judge type".format(sys.argv[0]))
53+
"{} set -- switch program language/judge method".format(sys.argv[0]))
5654
print(
57-
"{} compile -- compile source code".format(sys.argv[0]))
58-
55+
"{} codegen -- to generate code of the specified single problem".format(sys.argv[0]))
56+
print(
57+
"{} version -- show atcoder-tools version".format(sys.argv[0]))
5958
sys.exit(-1)
6059

6160
prog = " ".join(sys.argv[:2])

atcodertools/common/judgetype.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ def __init__(self,
6363
self.diff = diff
6464

6565
def _verify_sub(self, output: float, expected: float) -> bool:
66-
if self.error_type in [ErrorType.Absolute, ErrorType.AbsoluteOrRelative] and abs(expected - output) <= self.diff:
66+
if self.error_type in [ErrorType.Absolute, ErrorType.AbsoluteOrRelative] and abs(
67+
expected - output) <= self.diff:
6768
return True
68-
if self.error_type in [ErrorType.Relative, ErrorType.AbsoluteOrRelative] and self._calc_absolute(output, expected):
69+
if self.error_type in [ErrorType.Relative, ErrorType.AbsoluteOrRelative] and self._calc_absolute(output,
70+
expected):
6971
return True
7072
return False
7173

@@ -115,12 +117,13 @@ def get_judge_filename():
115117

116118

117119
class MultiSolutionJudge(Judge):
118-
def __init__(self, judge_code_lang="cpp"):
120+
121+
def __init__(self, judge_code_lang: 'Language'):
122+
119123
self.judge_type = JudgeType.MultiSolution
120124
self.judge_code_filename, self.judge_exec_filename = get_judge_filename()
121125

122-
from atcodertools.common.language import Language
123-
self.judge_code_lang = Language.from_name(judge_code_lang)
126+
self.judge_code_lang = judge_code_lang
124127

125128
def verify(self, output, expected):
126129
raise NotImplementedError()
@@ -135,16 +138,16 @@ def to_dict(self):
135138

136139
@classmethod
137140
def from_dict(cls, dic):
138-
r = MultiSolutionJudge(dic["judge_code_lang"])
139-
return r
141+
from atcodertools.common.language import Language
142+
return MultiSolutionJudge(Language.from_name(dic["judge_code_lang"]))
140143

141144

142145
class InteractiveJudge(Judge):
143-
def __init__(self, judge_code_lang="cpp"):
146+
147+
def __init__(self, judge_code_lang: 'Language'):
144148
self.judge_type = JudgeType.Interactive
145149
self.judge_code_filename, self.judge_exec_filename = get_judge_filename()
146-
from atcodertools.common.language import Language
147-
self.judge_code_lang = Language.from_name(judge_code_lang)
150+
self.judge_code_lang = judge_code_lang
148151

149152
def verify(self, output, expected):
150153
raise NotImplementedError()
@@ -159,5 +162,5 @@ def to_dict(self):
159162

160163
@classmethod
161164
def from_dict(cls, dic):
162-
r = InteractiveJudge(dic["judge_code_lang"])
163-
return r
165+
from atcodertools.common.language import Language
166+
return InteractiveJudge(Language.from_name(dic["judge_code_lang"]))

atcodertools/common/language.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,4 @@ def from_name(cls, name: str):
165165

166166

167167
ALL_LANGUAGES = [CPP, JAVA, RUST, PYTHON, NIM, DLANG, CSHARP]
168-
ALL_LANGUAGE_NAMES = [lang.display_name for lang in ALL_LANGUAGES]
168+
ALL_LANGUAGE_NAMES = [lang.name for lang in ALL_LANGUAGES]

atcodertools/config/config.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from os.path import expanduser
66
import toml
77
from colorama import Fore
8+
9+
from atcodertools.common.language import Language
810
from atcodertools.common.logging import logger
911

1012
from atcodertools.codegen.code_style_config import CodeStyleConfig, DEFAULT_LANGUAGE
@@ -143,11 +145,17 @@ def load(cls, fp: TextIO, args: Optional[ProgramArgs] = None):
143145
expanduser("~"), ".atcodertools.toml")
144146

145147

146-
def get_config(args: argparse.Namespace) -> Config:
148+
def get_config(args: argparse.Namespace, language: Language = None) -> Config:
147149
def _load(path: str) -> Config:
148150
logger.info("Going to load {} as config".format(path))
149151
with open(path, 'r') as f:
150-
return Config.load(f, ProgramArgs.load(args))
152+
program_args = ProgramArgs.load(args)
153+
154+
if language is not None:
155+
assert program_args.lang is None
156+
program_args.lang = language.name
157+
158+
return Config.load(f, program_args)
151159

152160
if args.config:
153161
return _load(args.config)

atcodertools/tools/codegen.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
from atcodertools.config.config import Config
2020
from atcodertools.constprediction.constants_prediction import predict_constants
2121
from atcodertools.fmtprediction.models.format_prediction_result import FormatPredictionResult
22-
from atcodertools.fmtprediction.predict_format import MultiplePredictionResultsError, NoPredictionResultError, predict_format
22+
from atcodertools.fmtprediction.predict_format import MultiplePredictionResultsError, NoPredictionResultError, \
23+
predict_format
2324
from atcodertools.tools import get_default_config_path
2425
from atcodertools.tools.envgen import USER_CONFIG_PATH, get_config, output_splitter
2526
from atcodertools.tools.utils import with_color

atcodertools/tools/compiler.py

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,50 @@
88
import pathlib
99

1010

11-
def _compile(code_filename: str, exec_filename: str, compile_cmd: str, cwd: str, force_compile: bool) -> bool:
11+
class BadStatusCodeException(Exception):
12+
pass
13+
14+
15+
def _compile(code_filename: str, exec_filename: str, compile_cmd: str, cwd: str, force_compile: bool) -> None:
1216
if not force_compile:
13-
code_p = pathlib.Path(cwd + '/' + code_filename)
14-
if os.path.exists(cwd + '/' + exec_filename):
15-
exec_p = pathlib.Path(cwd + '/' + exec_filename)
16-
else:
17-
exec_p = None
18-
if exec_p is not None and code_p.stat().st_mtime < exec_p.stat().st_mtime:
17+
code_path = pathlib.Path(os.path.join(cwd, code_filename))
18+
exec_path_name = os.path.join(cwd, exec_filename)
19+
20+
if os.path.exists(exec_path_name) and code_path.stat().st_mtime < pathlib.Path(exec_path_name).stat().st_mtime:
1921
print("No need to compile")
20-
return True
21-
print("Compileing: ")
22-
print(compile_cmd)
22+
return
23+
24+
print("Compiling... (command: `{}`)".format(compile_cmd))
2325
code, stdout = run_command_with_returncode(compile_cmd, cwd)
2426
print(stdout)
25-
if code == 0:
26-
return True
27-
else:
28-
return False
27+
if code != 0:
28+
raise BadStatusCodeException()
2929

3030

31-
def compile_main_and_judge_programs(metadata: Metadata, cwd="./", force_compile=False):
32-
valid = True
31+
def compile_main_and_judge_programs(metadata: Metadata, cwd="./", force_compile=False) -> None:
3332
lang = metadata.lang
34-
print("code file: ")
33+
print("[Main Program]")
3534
compile_cmd = lang.get_compile_command('main')
3635
code_filename = lang.get_code_filename('main')
3736
exec_filename = lang.get_exec_filename('main')
38-
code = _compile(code_filename, exec_filename,
39-
compile_cmd, cwd, force_compile)
40-
if not code:
41-
valid = False
37+
38+
try:
39+
_compile(code_filename, exec_filename, compile_cmd, cwd, force_compile)
40+
except BadStatusCodeException as e:
41+
raise e
42+
4243
if metadata.judge_method.judge_type in [JudgeType.MultiSolution, JudgeType.Interactive]:
43-
print("judge file: ")
44+
print("[Judge Program]")
4445
lang = metadata.judge_method.judge_code_lang
4546
compile_cmd = lang.get_compile_command('judge')
4647
code_filename = lang.get_code_filename('judge')
4748
exec_filename = lang.get_exec_filename('judge')
4849

49-
code = _compile(code_filename, exec_filename,
50-
compile_cmd, cwd, force_compile)
51-
if not code:
52-
valid = False
53-
return valid
50+
try:
51+
_compile(code_filename, exec_filename,
52+
compile_cmd, cwd, force_compile)
53+
except BadStatusCodeException as e:
54+
raise e
5455

5556

5657
def main(prog, args):

atcodertools/tools/models/metadata.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
import json
2+
from typing import Optional
23

34
from atcodertools.client.models.problem import Problem
4-
from atcodertools.common.judgetype import NormalJudge, DecimalJudge, MultiSolutionJudge, InteractiveJudge, Judge, NoJudgeTypeException
5+
from atcodertools.common.judgetype import NormalJudge, DecimalJudge, MultiSolutionJudge, InteractiveJudge, Judge, \
6+
NoJudgeTypeException
57
from atcodertools.common.language import Language
68

7-
89
DEFAULT_IN_EXAMPLE_PATTERN = 'in_*.txt'
910
DEFAULT_OUT_EXAMPLE_PATTERN = "out_*.txt"
1011

1112

1213
class Metadata:
1314

14-
def __init__(self, problem: Problem, code_filename: str, sample_in_pattern: str, sample_out_pattern: str,
15-
lang: Language, judge_method: Judge = NormalJudge()):
15+
def __init__(self,
16+
problem: Optional[Problem],
17+
code_filename: Optional[str],
18+
sample_in_pattern: str,
19+
sample_out_pattern: str,
20+
lang: Optional[Language],
21+
judge_method: Judge = NormalJudge()):
1622
self.problem = problem
1723
self.code_filename = code_filename
1824
self.sample_in_pattern = sample_in_pattern
@@ -56,16 +62,6 @@ def from_dict(cls, dic):
5662
judge_method=judge_method
5763
)
5864

59-
def default_metadata():
60-
return Metadata(
61-
problem=None,
62-
code_filename=None,
63-
sample_in_pattern=DEFAULT_IN_EXAMPLE_PATTERN,
64-
sample_out_pattern=DEFAULT_OUT_EXAMPLE_PATTERN,
65-
lang=None,
66-
judge_method=NormalJudge()
67-
)
68-
6965
@classmethod
7066
def load_from(cls, filename):
7167
with open(filename) as f:
@@ -75,3 +71,13 @@ def save_to(self, filename):
7571
with open(filename, 'w') as f:
7672
json.dump(self.to_dict(), f, indent=1, sort_keys=True)
7773
f.write('\n')
74+
75+
76+
DEFAULT_METADATA = Metadata(
77+
problem=None,
78+
code_filename=None,
79+
sample_in_pattern=DEFAULT_IN_EXAMPLE_PATTERN,
80+
sample_out_pattern=DEFAULT_OUT_EXAMPLE_PATTERN,
81+
lang=None,
82+
judge_method=NormalJudge()
83+
)

atcodertools/tools/setter.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import argparse
44
import os
55
import shutil
6-
from atcodertools.common.judgetype import NormalJudge, DecimalJudge, ErrorType, MultiSolutionJudge, InteractiveJudge, JudgeType, NoJudgeTypeException, DEFAULT_EPS
6+
from atcodertools.common.judgetype import NormalJudge, DecimalJudge, ErrorType, MultiSolutionJudge, InteractiveJudge, \
7+
JudgeType, NoJudgeTypeException, DEFAULT_EPS
8+
from atcodertools.common.logging import logger
79
from atcodertools.tools.models.metadata import Metadata
810
from atcodertools.common.language import Language, ALL_LANGUAGES
911
from atcodertools.tools.templates import get_default_judge_template_path
@@ -46,61 +48,69 @@ def main(prog, args):
4648

4749
args = parser.parse_args(args)
4850

49-
metadata = Metadata.load_from(args.dir + "/metadata.json")
50-
51-
new_judge_type = args.judge_type
52-
if new_judge_type in ["decimal", "absolute", "relative", "absolute_or_relative"]:
53-
new_judge_type = "decimal"
54-
if args.judge_type == "decimal":
55-
args.judge_type = "absolute_or_relative"
56-
57-
old_judge_type = metadata.judge_method.judge_type.value
58-
59-
if new_judge_type is not None and new_judge_type != old_judge_type:
60-
if new_judge_type == JudgeType.Normal.value:
61-
metadata.judge_method = NormalJudge()
62-
elif new_judge_type == JudgeType.Decimal.value:
63-
metadata.judge_method = DecimalJudge()
64-
elif new_judge_type == JudgeType.MultiSolution.value:
65-
metadata.judge_method = MultiSolutionJudge()
66-
elif new_judge_type == JudgeType.Interactive.value:
67-
metadata.judge_method = InteractiveJudge()
51+
old_metadata = Metadata.load_from(os.path.join(args.dir, "metadata.json"))
52+
53+
# Use the old metadata as base metadata.
54+
output_metadata = Metadata.load_from(
55+
os.path.join(args.dir, "metadata.json"))
56+
57+
if args.judge_type in ["absolute", "relative", "absolute_or_relative"]:
58+
new_metadata_judge_type = "decimal"
59+
else:
60+
new_metadata_judge_type = args.judge_type
61+
62+
old_metadata_judge_type = old_metadata.judge_method.judge_type.value
63+
64+
if new_metadata_judge_type is not None and new_metadata_judge_type != old_metadata_judge_type:
65+
if new_metadata_judge_type == JudgeType.Normal.value:
66+
output_metadata.judge_method = NormalJudge()
67+
elif new_metadata_judge_type == JudgeType.Decimal.value:
68+
output_metadata.judge_method = DecimalJudge()
69+
elif new_metadata_judge_type == JudgeType.MultiSolution.value:
70+
output_metadata.judge_method = MultiSolutionJudge(
71+
Language.from_name('cpp'))
72+
elif new_metadata_judge_type == JudgeType.Interactive.value:
73+
output_metadata.judge_method = InteractiveJudge(
74+
Language.from_name('cpp'))
6875
else:
6976
raise NoJudgeTypeException()
7077

71-
if new_judge_type == JudgeType.Decimal.value:
78+
if new_metadata_judge_type == JudgeType.Decimal.value:
7279
if args.error_value is not None:
73-
metadata.judge_method.diff = args.error_value
80+
output_metadata.judge_method.diff = args.error_value
7481
else:
75-
print("Warning: error-value is not specified default value is set. ")
76-
metadata.judge_method.error_type = ErrorType(args.judge_type)
77-
elif new_judge_type == JudgeType.MultiSolution.value:
82+
logger.warn(
83+
"Error-value is not specified. Default value will be set.")
84+
output_metadata.judge_method.error_type = ErrorType(args.judge_type)
85+
86+
elif new_metadata_judge_type == JudgeType.MultiSolution.value:
7887
if not os.path.exists("./judge.cpp"):
79-
print("touch ./judge.cpp (multi sotlution)")
88+
print("touch ./judge.cpp (multi solution)")
8089
judge_template_path = get_default_judge_template_path('cpp')
8190
shutil.copy(judge_template_path, "./judge.cpp")
8291
else:
83-
print("Judge Code exists")
84-
elif new_judge_type == JudgeType.Interactive.value:
85-
if not os.path.exists("/judge.cpp"):
92+
print("Judge code exists. Skipping creating judge code...")
93+
elif new_metadata_judge_type == JudgeType.Interactive.value:
94+
if not os.path.exists("./judge.cpp"):
8695
print("touch ./judge.cpp (interactive)")
8796
judge_template_path = get_default_judge_template_path('cpp')
8897
shutil.copy(judge_template_path, "./judge.cpp")
8998
else:
90-
print("Judge Code exists")
99+
print("Judge code exists. Skipping creating judge code...")
91100

92101
if args.lang is not None:
93-
if args.lang != metadata.lang.name:
94-
metadata.lang = Language.from_name(args.lang)
95-
metadata.code_filename = metadata.lang.get_code_filename('main')
102+
if args.lang != output_metadata.lang.name:
103+
output_metadata.lang = Language.from_name(args.lang)
104+
output_metadata.code_filename = output_metadata.lang.get_code_filename(
105+
'main')
96106
url = "https://atcoder.jp/contests/{}/tasks/{}".format(
97-
metadata.problem.contest.contest_id, metadata.problem.problem_id)
98-
if not os.path.exists(metadata.code_filename):
99-
codegen_main("", ["--lang", metadata.lang.name,
100-
url], open(metadata.code_filename, 'w'))
107+
output_metadata.problem.contest.contest_id, output_metadata.problem.problem_id)
108+
if not os.path.exists(output_metadata.code_filename):
109+
codegen_main("", ["--lang", output_metadata.lang.name,
110+
url], open(output_metadata.code_filename, 'w'))
101111
else:
102-
print("file exists: ", metadata.code_filename)
112+
print("File exists: ", output_metadata.code_filename)
103113
else:
104-
print("already set to {}".format(args.lang))
105-
metadata.save_to(args.dir + "/metadata.json")
106-
return metadata
114+
print("Already set to {}. Skipping changing language...".format(args.lang))
115+
output_metadata.save_to(os.path.join(args.dir, "metadata.json"))
116+
return output_metadata

0 commit comments

Comments
 (0)