Skip to content

Commit ccab876

Browse files
Refactor codes around language (#91)
* Refactor logics handling languages * autopep8 & flake8 * Fix a bug * default_template.rust -> default_template.rs * Stop wrapping type name with ''
1 parent 7f56f69 commit ccab876

File tree

11 files changed

+129
-70
lines changed

11 files changed

+129
-70
lines changed

atcodertools/client/atcoder.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import logging
33
import os
44
import re
5+
import warnings
56
from http.cookiejar import LWPCookieJar
6-
from typing import List, Optional, Tuple
7+
from typing import List, Optional, Tuple, Union
78

89
import requests
910
from bs4 import BeautifulSoup
1011

1112
from atcodertools.client.models.submission import Submission
13+
from atcodertools.common.language import Language
1214
from atcodertools.fileutils.artifacts_cache import get_cache_file_path
1315
from atcodertools.client.models.contest import Contest
1416
from atcodertools.client.models.problem import Problem
@@ -141,7 +143,16 @@ def download_all_contests(self) -> List[Contest]:
141143
contest_ids = sorted(contest_ids)
142144
return [Contest(contest_id) for contest_id in contest_ids]
143145

144-
def submit_source_code(self, contest: Contest, problem: Problem, lang: str, source: str) -> Submission:
146+
def submit_source_code(self, contest: Contest, problem: Problem, lang: Union[str, Language], source: str) -> Submission:
147+
if isinstance(lang, str):
148+
warnings.warn(
149+
"Parameter lang as a str object is deprecated. "
150+
"Please use 'atcodertools.common.language.Language' object instead",
151+
UserWarning)
152+
lang_option_pattern = lang
153+
else:
154+
lang_option_pattern = lang.submission_lang_pattern
155+
145156
resp = self._request(contest.get_submit_url())
146157

147158
soup = BeautifulSoup(resp.text, "html.parser")
@@ -155,7 +166,7 @@ def submit_source_code(self, contest: Contest, problem: Problem, lang: str, sour
155166
'select', attrs={"id": "submit-language-selector-{}".format(task_number)})
156167
language_field_name = language_select_area.get("name")
157168
language_number = language_select_area.find(
158-
"option", text=lang).get("value")
169+
"option", text=lang_option_pattern).get("value")
159170
postdata = {
160171
"__session": session_id,
161172
task_field_name: task_number,

atcodertools/codegen/code_style_config.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import os
33
from os.path import expanduser
44
from typing import Optional
5+
56
from atcodertools.fileutils.normalize import normalize_path
6-
from atcodertools.tools.templates import get_default_template_path
77

88
INDENT_TYPE_SPACE = 'space'
99
INDENT_TYPE_TAB = 'tab'
@@ -14,7 +14,6 @@ class CodeStyleConfigInitError(Exception):
1414

1515

1616
DEFAULT_WORKSPACE_DIR_PATH = os.path.join(expanduser("~"), "atcoder-workspace")
17-
SUPPORTED_LANGUAGES = ["cpp", "java", "rust"]
1817

1918

2019
class CodeStyleConfig:
@@ -27,9 +26,17 @@ def __init__(self,
2726
workspace_dir: Optional[str] = None,
2827
lang: str = "cpp",
2928
):
29+
from atcodertools.common.language import Language, LanguageNotFoundError, ALL_LANGUAGE_NAMES
30+
3031
code_generator_file = normalize_path(code_generator_file)
3132
template_file = normalize_path(template_file)
3233

34+
try:
35+
lang = Language.from_name(lang)
36+
except LanguageNotFoundError:
37+
raise CodeStyleConfigInitError(
38+
"language must be one of {}".format(ALL_LANGUAGE_NAMES))
39+
3340
if indent_type not in [INDENT_TYPE_SPACE, INDENT_TYPE_TAB]:
3441
raise CodeStyleConfigInitError(
3542
"indent_type must be 'space' or 'tab'")
@@ -48,10 +55,6 @@ def __init__(self,
4855
template_file)
4956
)
5057

51-
if lang and lang not in SUPPORTED_LANGUAGES:
52-
raise CodeStyleConfigInitError(
53-
"language must be one of {}".format(SUPPORTED_LANGUAGES))
54-
5558
self.indent_type = indent_type
5659
self.indent_width = indent_width
5760

@@ -64,19 +67,10 @@ def __init__(self,
6467
raise CodeStyleConfigInitError(e, "Error while loading {}".format(
6568
code_generator_file))
6669
else:
67-
assert lang is not None
68-
if lang == "cpp":
69-
from atcodertools.codegen.code_generators import cpp
70-
self.code_generator = cpp.main
71-
elif lang == "java":
72-
from atcodertools.codegen.code_generators import java
73-
self.code_generator = java.main
74-
else:
75-
from atcodertools.codegen.code_generators import rust
76-
self.code_generator = rust.main
70+
self.code_generator = lang.default_code_generator
7771

7872
self.template_file = normalize_path(
79-
template_file or get_default_template_path(lang))
73+
template_file or lang.default_template_path)
8074
self.workspace_dir = normalize_path(
8175
workspace_dir or DEFAULT_WORKSPACE_DIR_PATH)
8276
self.lang = lang

atcodertools/common/__init__.py

Whitespace-only changes.

atcodertools/common/language.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import re
2+
from typing import Pattern, Callable
3+
4+
from atcodertools.codegen.code_generators import cpp, java
5+
from atcodertools.codegen.models.code_gen_args import CodeGenArgs
6+
from atcodertools.tools.templates import get_default_template_path
7+
8+
9+
class LanguageNotFoundError(Exception):
10+
pass
11+
12+
13+
class Language:
14+
15+
def __init__(self,
16+
name: str,
17+
display_name: str,
18+
extension: str,
19+
submission_lang_pattern: Pattern[str],
20+
default_code_generator: Callable[[CodeGenArgs], str],
21+
default_template_path: str,
22+
):
23+
self.name = name
24+
self.display_name = display_name
25+
self.extension = extension
26+
self.submission_lang_pattern = submission_lang_pattern
27+
self.default_code_generator = default_code_generator
28+
self.default_template_path = default_template_path
29+
30+
@classmethod
31+
def from_name(cls, name: str):
32+
for l in ALL_LANGUAGES:
33+
if l.name == name:
34+
return l
35+
raise LanguageNotFoundError(
36+
"No language support for '{}'".format(ALL_LANGUAGE_NAMES))
37+
38+
39+
CPP = Language(
40+
name="cpp",
41+
display_name="C++",
42+
extension="cpp",
43+
submission_lang_pattern=re.compile(".*C\\+\\+14 \\(GCC.*"),
44+
default_code_generator=cpp.main,
45+
default_template_path=get_default_template_path('cpp'),
46+
)
47+
48+
JAVA = Language(
49+
name="java",
50+
display_name="Java",
51+
extension="java",
52+
submission_lang_pattern=re.compile(".*Java8.*"),
53+
default_code_generator=java.main,
54+
default_template_path=get_default_template_path('java'),
55+
)
56+
57+
RUST = Language(
58+
name="rust",
59+
display_name="Rust",
60+
extension="rs",
61+
submission_lang_pattern=re.compile(".*Rust \\(1.*"),
62+
default_code_generator=java.main,
63+
default_template_path=get_default_template_path('rs'),
64+
)
65+
66+
ALL_LANGUAGES = [CPP, JAVA, RUST]
67+
ALL_LANGUAGE_NAMES = [lang.display_name for lang in ALL_LANGUAGES]

atcodertools/config/config.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ def load(cls, fp: TextIO, args: Optional[Namespace] = None):
4141

4242
if args:
4343
code_style_config_dic = _update_config_dict(code_style_config_dic,
44-
dict(template_file=args.template,
45-
workspace_dir=args.workspace,
46-
lang=args.lang))
44+
dict(
45+
template_file=args.template,
46+
workspace_dir=args.workspace,
47+
lang=args.lang))
4748
etc_config_dic = _update_config_dict(etc_config_dic,
48-
dict(download_without_login=args.without_login,
49-
parallel_download=args.parallel,
50-
save_no_session_cache=args.save_no_session_cache))
49+
dict(
50+
download_without_login=args.without_login,
51+
parallel_download=args.parallel,
52+
save_no_session_cache=args.save_no_session_cache))
5153

5254
return Config(
5355
code_style_config=CodeStyleConfig(**code_style_config_dic),

atcodertools/tools/envgen.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
from atcodertools.client.atcoder import AtCoderClient, Contest, LoginError
1616
from atcodertools.client.models.problem import Problem
1717
from atcodertools.client.models.problem_content import InputFormatDetectionError, SampleDetectionError
18+
from atcodertools.codegen.code_style_config import DEFAULT_WORKSPACE_DIR_PATH
1819
from atcodertools.codegen.models.code_gen_args import CodeGenArgs
19-
from atcodertools.codegen.code_style_config import DEFAULT_WORKSPACE_DIR_PATH, SUPPORTED_LANGUAGES
20+
from atcodertools.common.language import ALL_LANGUAGES, CPP
2021
from atcodertools.config.config import Config
2122
from atcodertools.constprediction.constants_prediction import predict_constants
2223
from atcodertools.fileutils.create_contest_file import create_examples, \
@@ -26,10 +27,8 @@
2627
MultiplePredictionResultsError, predict_format
2728
from atcodertools.tools import get_default_config_path
2829
from atcodertools.tools.models.metadata import Metadata
29-
from atcodertools.tools.templates import get_default_template_path
3030
from atcodertools.tools.utils import with_color
3131

32-
3332
fmt = "%(asctime)s %(levelname)s: %(message)s"
3433
logging.basicConfig(level=logging.INFO, format=fmt)
3534

@@ -38,12 +37,6 @@ class BannedFileDetectedError(Exception):
3837
pass
3938

4039

41-
def extension(lang: str):
42-
if lang == 'rust':
43-
return 'rs'
44-
return lang
45-
46-
4740
IN_EXAMPLE_FORMAT = "in_{}.txt"
4841
OUT_EXAMPLE_FORMAT = "out_{}.txt"
4942

@@ -102,7 +95,7 @@ def emit_info(text):
10295

10396
code_file_path = os.path.join(
10497
problem_dir_path,
105-
"main.{}".format(extension(lang)))
98+
"main.{}".format(lang.extension))
10699

107100
# If there is an existing code, just create backup
108101
if os.path.exists(code_file_path):
@@ -121,7 +114,8 @@ def emit_info(text):
121114

122115
try:
123116
prediction_result = predict_format(content)
124-
emit_info(with_color("Format prediction succeeded", Fore.LIGHTGREEN_EX))
117+
emit_info(
118+
with_color("Format prediction succeeded", Fore.LIGHTGREEN_EX))
125119
except (NoPredictionResultError, MultiplePredictionResultsError) as e:
126120
prediction_result = FormatPredictionResult.empty_result()
127121
if isinstance(e, NoPredictionResultError):
@@ -210,8 +204,6 @@ def prepare_contest(atcoder_client: AtCoderClient,
210204
contest_dir_path)
211205

212206

213-
DEFAULT_LANG = "cpp"
214-
215207
USER_CONFIG_PATH = os.path.join(
216208
expanduser("~"), ".atcodertools.toml")
217209

@@ -254,16 +246,16 @@ def main(prog, args):
254246

255247
parser.add_argument("--lang",
256248
help="Programming language of your template code, {}.\n"
257-
.format(" or ".join(SUPPORTED_LANGUAGES)) + "[Default] {}".format(DEFAULT_LANG))
249+
.format(" or ".join([lang.name for lang in ALL_LANGUAGES])) + "[Default] {}".format(CPP.name))
258250

259251
parser.add_argument("--template",
260-
help="File path to your template code\n{0}{1}".format(
261-
"[Default (C++)] {}\n".format(
262-
get_default_template_path('cpp')),
263-
"[Default (Java)] {}".format(
264-
get_default_template_path('java')),
265-
"[Default (Rust)] {}".format(
266-
get_default_template_path('rust'))),
252+
help="File path to your template code\n{}".format(
253+
"\n".join(
254+
["[Default ({dname})] {path}".format(
255+
dname=lang.display_name,
256+
path=lang.default_template_path
257+
) for lang in ALL_LANGUAGES]
258+
))
267259
)
268260

269261
# Deleted functionality

atcodertools/tools/models/metadata.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import json
22

33
from atcodertools.client.models.problem import Problem
4+
from atcodertools.common.language import Language
45

56

67
class Metadata:
78

8-
def __init__(self, problem: Problem, code_filename: str, sample_in_pattern: str, sample_out_pattern: str, lang: str):
9+
def __init__(self, problem: Problem, code_filename: str, sample_in_pattern: str, sample_out_pattern: str, lang: Language):
910
self.problem = problem
1011
self.code_filename = code_filename
1112
self.sample_in_pattern = sample_in_pattern
@@ -18,7 +19,7 @@ def to_dict(self):
1819
"code_filename": self.code_filename,
1920
"sample_in_pattern": self.sample_in_pattern,
2021
"sample_out_pattern": self.sample_out_pattern,
21-
"lang": self.lang,
22+
"lang": self.lang.name,
2223
}
2324

2425
@classmethod
@@ -28,7 +29,7 @@ def from_dict(cls, dic):
2829
code_filename=dic["code_filename"],
2930
sample_in_pattern=dic["sample_in_pattern"],
3031
sample_out_pattern=dic["sample_out_pattern"],
31-
lang=dic["lang"],
32+
lang=Language.from_name(dic["lang"]),
3233
)
3334

3435
@classmethod

atcodertools/tools/submit.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import os
66

77
from colorama import Fore
8-
98
from atcodertools.tools.utils import with_color
109

1110
from atcodertools.client.atcoder import AtCoderClient, LoginError
@@ -14,16 +13,6 @@
1413
from atcodertools.tools.models.metadata import Metadata
1514

1615

17-
def infer_detailed_lang(lang: str):
18-
if lang == "java":
19-
return "Java8 (OpenJDK 1.8.0)"
20-
if lang == "cpp":
21-
return "C++14 (GCC 5.4.1)"
22-
if lang == "rust":
23-
return "Rust (1.15.1)"
24-
raise NotImplementedError
25-
26-
2716
def main(prog, args, credential_supplier=None, use_local_session_cache=True) -> bool:
2817
parser = argparse.ArgumentParser(
2918
prog=prog,
@@ -104,10 +93,10 @@ def main(prog, args, credential_supplier=None, use_local_session_cache=True) ->
10493
code_path = args.code or os.path.join(args.dir, metadata.code_filename)
10594
with open(code_path, 'r') as f:
10695
source = f.read()
107-
detailed_lang = infer_detailed_lang(metadata.lang)
108-
logging.info("Submitting {} as {}".format(code_path, detailed_lang))
96+
logging.info(
97+
"Submitting {} as {}".format(code_path, metadata.lang.name))
10998
submission = client.submit_source_code(
110-
metadata.problem.contest, metadata.problem, detailed_lang, source)
99+
metadata.problem.contest, metadata.problem, metadata.lang, source)
111100
logging.info("{} {}".format(
112101
with_color("Done!", Fore.LIGHTGREEN_EX),
113102
metadata.problem.contest.get_submissions_url(submission)))

atcodertools/tools/templates/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
DEFAULT_TEMPLATE_DIR_PATH = os.path.dirname(os.path.abspath(__file__))
44

55

6-
def get_default_template_path(lang: str):
7-
return os.path.abspath(os.path.join(DEFAULT_TEMPLATE_DIR_PATH, "default_template.{}".format(lang)))
6+
def get_default_template_path(extension: str):
7+
return os.path.abspath(os.path.join(DEFAULT_TEMPLATE_DIR_PATH, "default_template.{}".format(extension)))
File renamed without changes.

0 commit comments

Comments
 (0)