Skip to content

Commit 06ad3cf

Browse files
Delete --replacement and use template for both failure and success instead (#79)
* Obsolete --replacement * Delete logic relying on replacement * New templates for both failure and success * Rename template files * Fix tests * Update README * forgot to add unit test files * Autopep8
1 parent 6c7a649 commit 06ad3cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+217
-236
lines changed

README.md

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,11 @@ $ atcoder-tool gen [contest_id] --without-login
5050

5151
### gen の詳細
5252
```$xslt
53-
usage: atcoder-tools gen [-h] [--without-login]
54-
[--workspace WORKSPACE]
55-
[--lang LANG]
56-
[--template TEMPLATE]
57-
[--replacement REPLACEMENT]
58-
[--parallel]
59-
[--save-no-session-cache]
60-
[--config CONFIG]
61-
contest_id
53+
usage: atcoder-tools gen
54+
[-h] [--without-login] [--workspace WORKSPACE] [--lang LANG]
55+
[--template TEMPLATE] [--parallel] [--save-no-session-cache]
56+
[--config CONFIG]
57+
contest_id
6258
6359
positional arguments:
6460
contest_id Contest ID (e.g. arc001)
@@ -68,23 +64,18 @@ optional arguments:
6864
--without-login Download data without login
6965
--workspace WORKSPACE
7066
Path to workspace's root directory. This script will create files in {WORKSPACE}/{contest_name}/{alphabet}/ e.g. ./your-workspace/arc001/A/
71-
[Default] ${HOME}/atcoder-workspace
67+
[Default] /home/kyuridenamida/atcoder-workspace
7268
--lang LANG Programming language of your template code, cpp or java.
7369
[Default] cpp
7470
--template TEMPLATE File path to your template code
75-
[Default (C++)] /atcodertools/tools/templates/cpp/template_success.cpp
76-
[Default (Java)] /atcodertools/tools/templates/java/template_success.java
77-
--replacement REPLACEMENT
78-
File path to your config file
79-
[Default (C++)] /atcodertools/tools/templates/cpp/template_failure.cpp
80-
[Default (Java)] /atcodertools/tools/templates/java/template_failure.java
71+
[Default (C++)] /atcoder-tools/atcodertools/tools/templates/cpp/default_template.cpp
72+
[Default (Java)] /atcoder-tools/atcodertools/tools/templates/java/default_template.java
8173
--parallel Prepare problem directories asynchronously using multi processors.
8274
--save-no-session-cache
8375
Save no session cache to avoid security risk
8476
--config CONFIG File path to your config file
85-
[Default (Primary)] ${HOME}/.atcodertools.toml
86-
[Default (Secondary)] /atcodertools/tools/atcodertools-default.toml
87-
77+
[Default (Primary)] /home/kyuridenamida/.atcodertools.toml
78+
[Default (Secondary)] /atcoder-tools/atcodertools/tools/atcodertools-default.toml
8879
```
8980

9081
### test の詳細
@@ -160,31 +151,48 @@ code_generator_file="~/custom_code_generator.py"
160151
`(CogeGenArgs) -> str(ソースコード)`が型であるような`main`関数を定義した.pyファイルを`code_generator_file`で指定すると、コード生成時にカスタムコードジェネレーターを利用できます。
161152

162153
## テンプレートの例
163-
`atcoder-tools gen`コマンドに対し`--template`, `--replacement` でそれぞれ入力形式の推論に成功したときのテンプレート、生成に失敗したときに代わりに生成するソースコードを指定できます。テンプレートエンジンの仕様については[jinja2](http://jinja.pocoo.org/docs/2.10/) の公式ドキュメントを参照してください。テンプレートに渡される変数は以下の通りです。
154+
`atcoder-tools gen`コマンドに対し`--template`でテンプレートソースコードを指定できます。
155+
テンプレートエンジンの仕様については[jinja2](http://jinja.pocoo.org/docs/2.10/) の公式ドキュメントを参照してください。
156+
157+
テンプレートに渡される変数は以下の通りです。
158+
164159

165-
- **input_part** input用のコード
166-
- **formal_arguments** 型つき引数列
167-
- **actual_arguments** 型なし引数列
160+
- **prediction_success** 入力形式の推論に成功したとき `True`、 失敗したとき `False`が格納されている。この値が`True`のとき次の3種類の変数も存在することが保証される。
161+
- **input_part** input用のコード
162+
- **formal_arguments** 型つき引数列
163+
- **actual_arguments** 型なし引数列
168164

169-
- **mod** 問題文中に存在するmodの値
170-
- **yes_str** 問題文中に存在する yes や possible などの真を表しそうな値
171-
- **no_str** 問題文中に存在する no や impossible などの偽を表しそうな値
165+
- **mod** 問題文中に存在するmodの整数値
166+
- **yes_str** 問題文中に存在する yes や possible などの真を表しそうな文字列値
167+
- **no_str** 問題文中に存在する no や impossible などの偽を表しそうな文字列値
172168

173169
```
174170
#include <bits/stdc++.h>
175171
using namespace std;
176172
177-
{% if mod %}const int mod = {{ mod }};{% endif %}
178-
{% if yes_str %}const string YES = "{{ yes_str }}";{% endif %}
179-
{% if no_str %}const string NO = "{{ no_str }}";{% endif %}
173+
{% if mod %}
174+
const long long MOD = {{ mod }};
175+
{% endif %}
176+
{% if yes_str %}
177+
const string YES = "{{ yes_str }}";
178+
{% endif %}
179+
{% if no_str %}
180+
const string NO = "{{ no_str }}";
181+
{% endif %}
180182
181-
void solve({{formal_arguments}}){
183+
{% if prediction_success %}
184+
void solve({{ formal_arguments }}){
182185
183186
}
187+
{% endif %}
184188
185189
int main(){
190+
{% if prediction_success %}
186191
{{input_part}}
187-
solve({{actual_arguments}});
192+
solve({{ actual_arguments }});
193+
{% else %}
194+
// Failed to predict input format
195+
{% endif %}
188196
return 0;
189197
}
190198
```

atcodertools/codegen/code_generators/cpp.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict, Any
1+
from typing import Dict, Any, Optional
22

33
from atcodertools.codegen.code_style_config import CodeStyleConfig
44
from atcodertools.codegen.models.code_gen_args import CodeGenArgs
@@ -26,15 +26,19 @@ def _loop_header(var: Variable, for_second_index: bool):
2626
class CppCodeGenerator:
2727

2828
def __init__(self,
29-
format_: Format[Variable],
29+
format_: Optional[Format[Variable]],
3030
config: CodeStyleConfig):
3131
self._format = format_
3232
self._config = config
3333

3434
def generate_parameters(self) -> Dict[str, Any]:
35+
if self._format is None:
36+
return dict(prediction_success=False)
37+
3538
return dict(formal_arguments=self._formal_arguments(),
3639
actual_arguments=self._actual_arguments(),
37-
input_part=self._input_part())
40+
input_part=self._input_part(),
41+
prediction_success=True)
3842

3943
def _input_part(self):
4044
lines = []

atcodertools/codegen/code_style_config.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import importlib.machinery as imm
22
import os
3+
from typing import Optional
34

45
from atcodertools.fileutils.normalize import normalize_path
56

@@ -16,8 +17,8 @@ class CodeStyleConfig:
1617
def __init__(self,
1718
indent_type: str = INDENT_TYPE_SPACE,
1819
indent_width: int = 4,
19-
code_generator_file: str = None,
20-
template_file: str = None,
20+
code_generator_file: Optional[str] = None,
21+
template_file: Optional[str] = None,
2122
):
2223
code_generator_file = normalize_path(code_generator_file)
2324
template_file = normalize_path(template_file)

atcodertools/codegen/models/code_gen_args.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Optional
2+
13
from atcodertools.codegen.code_style_config import CodeStyleConfig
24
from atcodertools.constprediction.models.problem_constant_set import ProblemConstantSet
35
from atcodertools.fmtprediction.models.format import Format
@@ -8,7 +10,7 @@ class CodeGenArgs:
810

911
def __init__(self,
1012
template: str,
11-
format_: Format[Variable],
13+
format_: Optional[Format[Variable]],
1214
constants: ProblemConstantSet,
1315
config: CodeStyleConfig):
1416
self.template = template

atcodertools/codegen/template_engine.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import string
21
import re
2+
import string
33
import warnings
44

5-
import jinja2
5+
from jinja2 import Environment
66

77

88
def _substitute(s, reps):
@@ -47,5 +47,5 @@ def old_render(template, **kwargs):
4747

4848

4949
def render_by_jinja(template, **kwargs):
50-
template = jinja2.Template(template)
51-
return template.render(**kwargs) + "\n"
50+
return Environment(trim_blocks=True,
51+
lstrip_blocks=True).from_string(template).render(**kwargs) + "\n"

atcodertools/fileutils/create_contest_file.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66

77
def _make_text_file(file_path, text):
8+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
89
with open(file_path, 'w') as f:
910
f.write(text)
1011

atcodertools/fmtprediction/models/format_prediction_result.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Dict
1+
from typing import Dict, Optional
22

33
from atcodertools.fmtprediction.models.format import Format
44
from atcodertools.fmtprediction.models.type import Type
@@ -7,7 +7,7 @@
77

88
class FormatPredictionResult:
99

10-
def __init__(self, format_: Format[Variable]):
10+
def __init__(self, format_: Optional[Format[Variable]] = None):
1111
self.format = format_
1212

1313
@classmethod
@@ -28,3 +28,7 @@ def create_typed_format(cls, simple_format: Format[SimpleVariable], var_to_type:
2828
fmt.push_back(pattern.with_replaced_vars(var_to_info))
2929

3030
return FormatPredictionResult(fmt)
31+
32+
@classmethod
33+
def empty_result(cls):
34+
return FormatPredictionResult()

atcodertools/tools/envgen.py

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from atcodertools.constprediction.constants_prediction import predict_constants
2222
from atcodertools.fileutils.create_contest_file import create_examples, \
2323
create_code
24+
from atcodertools.fmtprediction.models.format_prediction_result import FormatPredictionResult
2425
from atcodertools.fmtprediction.predict_format import NoPredictionResultError, \
2526
MultiplePredictionResultsError, predict_format
2627
from atcodertools.tools.models.metadata import Metadata
@@ -70,7 +71,6 @@ def prepare_procedure(atcoder_client: AtCoderClient,
7071
problem: Problem,
7172
workspace_root_path: str,
7273
template_code_path: str,
73-
replacement_code_path: str,
7474
lang: str,
7575
config: Config):
7676
pid = problem.get_alphabet()
@@ -129,40 +129,30 @@ def emit_info(text):
129129
new_path))
130130

131131
try:
132-
133-
with open(template_code_path, "r") as f:
134-
template = f.read()
135-
136-
result = predict_format(content)
137-
constants = predict_constants(content.original_html)
138-
139-
code_generator = _decide_code_generator(config, lang)
140-
create_code(code_generator(
141-
CodeGenArgs(
142-
template,
143-
result.format,
144-
constants,
145-
config.code_style_config
146-
)),
147-
code_file_path
148-
)
149-
emit_info(
150-
"{} -- Saved auto-generated code to '{}'".format(
151-
with_color("Prediction succeeded", Fore.LIGHTGREEN_EX),
152-
code_file_path))
132+
prediction_result = predict_format(content)
133+
emit_info(with_color("Format prediction succeeded", Fore.LIGHTGREEN_EX))
153134
except (NoPredictionResultError, MultiplePredictionResultsError) as e:
135+
prediction_result = FormatPredictionResult.empty_result()
154136
if isinstance(e, NoPredictionResultError):
155137
msg = "No prediction -- Failed to understand the input format"
156138
else:
157139
msg = "Too many prediction -- Failed to understand the input format"
158-
159-
os.makedirs(os.path.dirname(code_file_path), exist_ok=True)
160-
shutil.copy(replacement_code_path, code_file_path)
161-
emit_warning(
162-
"{} -- Copied {} to {}".format(
163-
with_color(msg, Fore.LIGHTRED_EX),
164-
replacement_code_path,
165-
code_file_path))
140+
emit_warning(with_color(msg, Fore.LIGHTRED_EX))
141+
142+
constants = predict_constants(content.original_html)
143+
code_generator = _decide_code_generator(config, lang)
144+
with open(template_code_path, "r") as f:
145+
template = f.read()
146+
147+
create_code(code_generator(
148+
CodeGenArgs(
149+
template,
150+
prediction_result.format,
151+
constants,
152+
config.code_style_config
153+
)),
154+
code_file_path)
155+
emit_info("Saved code to {}".format(code_file_path))
166156

167157
# Save metadata
168158
metadata_path = os.path.join(problem_dir_path, "metadata.json")
@@ -183,18 +173,17 @@ def emit_info(text):
183173
output_splitter()
184174

185175

186-
def func(argv: Tuple[AtCoderClient, Problem, str, str, str, str, Config]):
187-
atcoder_client, problem, workspace_root_path, template_code_path, replacement_code_path, lang, config = argv
176+
def func(argv: Tuple[AtCoderClient, Problem, str, str, str, Config]):
177+
atcoder_client, problem, workspace_root_path, template_code_path, lang, config = argv
188178
prepare_procedure(
189179
atcoder_client, problem, workspace_root_path, template_code_path,
190-
replacement_code_path, lang, config)
180+
lang, config)
191181

192182

193183
def prepare_contest(atcoder_client: AtCoderClient,
194184
contest_id: str,
195185
workspace_root_path: str,
196186
template_code_path: str,
197-
replacement_code_path: str,
198187
lang: str,
199188
parallel: bool,
200189
config: Config,
@@ -209,7 +198,7 @@ def prepare_contest(atcoder_client: AtCoderClient,
209198
logging.warning(
210199
"Failed to fetch. Will retry in {} seconds".format(retry_duration))
211200

212-
tasks = [(atcoder_client, problem, workspace_root_path, template_code_path, replacement_code_path, lang, config) for
201+
tasks = [(atcoder_client, problem, workspace_root_path, template_code_path, lang, config) for
213202
problem in problem_list]
214203

215204
output_splitter()
@@ -242,11 +231,7 @@ def prepare_contest(atcoder_client: AtCoderClient,
242231

243232

244233
def get_default_template_path(lang):
245-
return os.path.abspath(os.path.join(DEFAULT_TEMPLATE_DIR_PATH, "{lang}/template_success.{lang}".format(lang=lang)))
246-
247-
248-
def get_default_replacement_path(lang):
249-
return os.path.abspath(os.path.join(DEFAULT_TEMPLATE_DIR_PATH, "{lang}/template_failure.{lang}").format(lang=lang))
234+
return os.path.abspath(os.path.join(DEFAULT_TEMPLATE_DIR_PATH, "{lang}/default_template.{lang}".format(lang=lang)))
250235

251236

252237
def decide_template_path(lang: str, config: Config, cmd_template_path: str):
@@ -290,6 +275,10 @@ def _load(path: str) -> Config:
290275
return _load(DEFAULT_CONFIG_PATH)
291276

292277

278+
class DeletedFunctionalityError(Exception):
279+
pass
280+
281+
293282
def main(prog, args):
294283
parser = argparse.ArgumentParser(
295284
prog=prog,
@@ -322,13 +311,8 @@ def main(prog, args):
322311
get_default_template_path('java')))
323312
)
324313

325-
parser.add_argument("--replacement",
326-
help="File path to your config file\n{0}{1}".format(
327-
"[Default (C++)] {}\n".format(
328-
get_default_replacement_path('cpp')),
329-
"[Default (Java)] {}".format(
330-
get_default_replacement_path('java')))
331-
)
314+
# Deleted functionality
315+
parser.add_argument('--replacement', help=argparse.SUPPRESS)
332316

333317
parser.add_argument("--parallel",
334318
action="store_true",
@@ -349,6 +333,12 @@ def main(prog, args):
349333

350334
args = parser.parse_args(args)
351335

336+
if args.replacement is not None:
337+
logging.error(with_color("Sorry! --replacement argument no longer exists"
338+
" and you can only use --template."
339+
" See the official document for details.", Fore.LIGHTRED_EX))
340+
raise DeletedFunctionalityError
341+
352342
try:
353343
import AccountInformation # noqa
354344
raise BannedFileDetectedError(
@@ -373,8 +363,6 @@ def main(prog, args):
373363
args.contest_id,
374364
args.workspace,
375365
decide_template_path(args.lang, config, args.template),
376-
args.replacement if args.replacement is not None else get_default_replacement_path(
377-
args.lang),
378366
args.lang,
379367
args.parallel,
380368
config

0 commit comments

Comments
 (0)