kattis-cli 1.0.3__tar.gz → 1.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/PKG-INFO +2 -2
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/download.py +0 -2
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/kattis.py +2 -2
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/main.py +7 -7
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/test_solution.py +23 -38
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/utils/config.py +1 -1
- kattis_cli-1.0.3/kattis_cli/utils/utility.py → kattis_cli-1.0.5/kattis_cli/utils/languages.py +2 -56
- kattis_cli-1.0.5/kattis_cli/utils/utility.py +94 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/pyproject.toml +2 -2
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/LICENSE +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/README.md +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/.kattis-cli.toml +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/__init__.py +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/kattis_setup.py +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/settings.py +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/ui.py +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/utils/__init__.py +0 -0
- {kattis_cli-1.0.3 → kattis_cli-1.0.5}/kattis_cli/utils/run_program.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kattis-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: A command-line tool for Kattis
|
|
5
5
|
Home-page: https://github.com/rambasnet/kattis-cli
|
|
6
6
|
Author: Ram Basnet
|
|
7
|
-
Author-email:
|
|
7
|
+
Author-email: rbasnet@coloradomesa.edu
|
|
8
8
|
Requires-Python: >=3.8,<4.0
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
and save them to data folders.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
# import sys
|
|
6
5
|
import shutil
|
|
7
6
|
from typing import Any, Dict
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
import requests
|
|
10
9
|
import yaml
|
|
11
10
|
from bs4 import BeautifulSoup
|
|
12
|
-
|
|
13
11
|
from .utils import config, utility
|
|
14
12
|
from . import settings
|
|
15
13
|
|
|
@@ -20,7 +20,7 @@ from rich.align import Align
|
|
|
20
20
|
from rich.live import Live
|
|
21
21
|
from rich.prompt import Confirm
|
|
22
22
|
|
|
23
|
-
from kattis_cli.utils import
|
|
23
|
+
from kattis_cli.utils import languages
|
|
24
24
|
from kattis_cli.utils import config
|
|
25
25
|
from kattis_cli import ui
|
|
26
26
|
|
|
@@ -182,7 +182,7 @@ def confirm_or_die(problem: str, language: str,
|
|
|
182
182
|
console.print('Language:', language)
|
|
183
183
|
console.print('Files:', ', '.join(files))
|
|
184
184
|
if mainclass:
|
|
185
|
-
if language in
|
|
185
|
+
if language in languages.GUESS_MAINFILE:
|
|
186
186
|
console.print('Main file:', mainclass)
|
|
187
187
|
else:
|
|
188
188
|
console.print('Mainclass:', mainclass)
|
|
@@ -7,7 +7,7 @@ build.sh script copies the contents of this file to main.py.
|
|
|
7
7
|
Change the __version__ to match in pyproject.toml
|
|
8
8
|
Has to be higher than the pypi version.
|
|
9
9
|
"""
|
|
10
|
-
__version__ = '1.0.
|
|
10
|
+
__version__ = '1.0.5'
|
|
11
11
|
|
|
12
12
|
from math import inf
|
|
13
13
|
from typing import Tuple
|
|
@@ -18,7 +18,7 @@ import kattis_cli.download as download
|
|
|
18
18
|
import kattis_cli.ui as ui
|
|
19
19
|
import kattis_cli.test_solution as test_solution
|
|
20
20
|
import kattis_cli.kattis as kattis
|
|
21
|
-
import kattis_cli.utils.
|
|
21
|
+
import kattis_cli.utils.languages as languages
|
|
22
22
|
import kattis_cli.kattis_setup as kattis_setup
|
|
23
23
|
|
|
24
24
|
|
|
@@ -75,11 +75,11 @@ def test(
|
|
|
75
75
|
"""Test solution with sample files.
|
|
76
76
|
"""
|
|
77
77
|
problemid, loc_language, mainclass, _files, root_folder, lang_config = \
|
|
78
|
-
|
|
78
|
+
languages.update_args(problemid, language, mainclass, list(files))
|
|
79
79
|
# print('After - ', f'{problemid=} {language=} {mainclass=} {_files=}')
|
|
80
80
|
# lang_config = config.parse_config(language)
|
|
81
81
|
if not mainclass:
|
|
82
|
-
mainclass =
|
|
82
|
+
mainclass = languages.guess_mainfile(
|
|
83
83
|
language, _files, problemid, lang_config)
|
|
84
84
|
|
|
85
85
|
test_solution.test_samples(
|
|
@@ -111,14 +111,14 @@ def submit(problemid: str, language: str,
|
|
|
111
111
|
"""Submit a solution to Kattis.
|
|
112
112
|
"""
|
|
113
113
|
problemid, language, mainclass, _files, _, lang_config = \
|
|
114
|
-
|
|
114
|
+
languages.update_args(
|
|
115
115
|
problemid, language, mainclass, list(files))
|
|
116
116
|
# Finally, submit the solution
|
|
117
117
|
# print(f'{problemid=} {language=} {mainclass=} {tag=} {force=} {_files=}')
|
|
118
118
|
if not mainclass:
|
|
119
|
-
mainclass =
|
|
119
|
+
mainclass = languages.guess_mainfile(
|
|
120
120
|
language, _files, problemid, lang_config)
|
|
121
|
-
kat_lang =
|
|
121
|
+
kat_lang = languages.LOCAL_TO_KATTIS[language]
|
|
122
122
|
kattis.submit_solution(_files, problemid,
|
|
123
123
|
kat_lang, mainclass,
|
|
124
124
|
tag, force)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Tester module for Kattis.
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
|
-
from math import inf
|
|
5
4
|
from typing import Any, List, Dict
|
|
5
|
+
from math import inf
|
|
6
6
|
import glob
|
|
7
7
|
import time
|
|
8
8
|
import os
|
|
@@ -17,26 +17,7 @@ from rich.prompt import Confirm
|
|
|
17
17
|
from rich.markup import escape
|
|
18
18
|
|
|
19
19
|
from kattis_cli import kattis
|
|
20
|
-
from kattis_cli.utils import run_program, utility
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def compare_floats(expected: str, ans: str, places: float) -> bool:
|
|
24
|
-
"""Compare two floating point numbers with given accuracy.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
expected (str): expected result
|
|
28
|
-
ans (str): actual result
|
|
29
|
-
places (float): decimal places for approximation
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
bool: True if the two numbers are equal within the given accuracy
|
|
33
|
-
"""
|
|
34
|
-
try:
|
|
35
|
-
flt_expected = float(expected)
|
|
36
|
-
flt_ans = float(ans)
|
|
37
|
-
return abs(flt_expected - flt_ans) <= 10**(-places)
|
|
38
|
-
except ValueError:
|
|
39
|
-
return False
|
|
20
|
+
from kattis_cli.utils import languages, run_program, utility
|
|
40
21
|
|
|
41
22
|
|
|
42
23
|
def test_samples(
|
|
@@ -138,27 +119,31 @@ def test_samples(
|
|
|
138
119
|
input_content = f.read()
|
|
139
120
|
input_content.replace(b'\r\n', b'\n') # Windows fix
|
|
140
121
|
out_file = in_file.replace('.in', '.ans')
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
122
|
+
try:
|
|
123
|
+
with open(out_file, 'rb') as f:
|
|
124
|
+
expected = f.read()
|
|
125
|
+
expected.replace(b'\r\n', b'\n')
|
|
126
|
+
except FileNotFoundError:
|
|
127
|
+
try:
|
|
128
|
+
out_file = in_file.replace('.in', '.out')
|
|
129
|
+
with open(out_file, 'rb') as f:
|
|
130
|
+
expected = f.read()
|
|
131
|
+
expected.replace(b'\r\n', b'\n')
|
|
132
|
+
except FileNotFoundError:
|
|
133
|
+
expected = b"No .ans or .out file found!"
|
|
144
134
|
# Run the program
|
|
145
135
|
code, ans, error = run_program.run(lang_config, mainclass, in_file)
|
|
146
|
-
expected = expected.strip()
|
|
147
136
|
if code != 0:
|
|
148
137
|
ans = error
|
|
149
138
|
# console.print(f"{ans=} {error=}")
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
else:
|
|
157
|
-
|
|
158
|
-
result = "[bold green]✅[/bold green]"
|
|
159
|
-
count += 1
|
|
160
|
-
else:
|
|
161
|
-
result = "[bold red]❌[/bold red]"
|
|
139
|
+
|
|
140
|
+
# floating point comparison
|
|
141
|
+
if utility.check_answer(expected.decode('utf-8'),
|
|
142
|
+
ans, accuracy):
|
|
143
|
+
result = "[bold green]✅[/bold green]"
|
|
144
|
+
count += 1
|
|
145
|
+
else:
|
|
146
|
+
result = "[bold red]❌[/bold red]"
|
|
162
147
|
|
|
163
148
|
# UI Table Row ---
|
|
164
149
|
in_filename = Path(in_file).parts[-1]
|
|
@@ -188,7 +173,7 @@ def test_samples(
|
|
|
188
173
|
"Awesome... Time to submit it to :cat: Kattis! :cat:",
|
|
189
174
|
style="bold green")
|
|
190
175
|
if Confirm.ask("Submit to Kattis?", default=True):
|
|
191
|
-
kat_language =
|
|
176
|
+
kat_language = languages.LOCAL_TO_KATTIS.get(loc_language, '')
|
|
192
177
|
kattis.submit_solution(files, problemid,
|
|
193
178
|
kat_language, mainclass,
|
|
194
179
|
tag="", force=True)
|
kattis_cli-1.0.3/kattis_cli/utils/utility.py → kattis_cli-1.0.5/kattis_cli/utils/languages.py
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"""Module for language utilities.
|
|
2
2
|
"""
|
|
3
3
|
|
|
4
|
-
from typing import List,
|
|
4
|
+
from typing import List, Dict, Any
|
|
5
5
|
import sys
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
import yaml
|
|
10
9
|
from rich.console import Console
|
|
11
10
|
from kattis_cli.utils import config
|
|
11
|
+
from kattis_cli.utils.utility import find_problem_root_folder
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
LANGUAGE_GUESS = {
|
|
@@ -248,60 +248,6 @@ def valid_extension(file: str) -> bool:
|
|
|
248
248
|
return False
|
|
249
249
|
|
|
250
250
|
|
|
251
|
-
def find_problem_root_folder(
|
|
252
|
-
cur_dir_path: Union[str, Path],
|
|
253
|
-
filename: str
|
|
254
|
-
) -> Path:
|
|
255
|
-
"""Find the root problem folder given a directory path and filename.
|
|
256
|
-
|
|
257
|
-
Args:
|
|
258
|
-
cur_dir_path (Union[str, Path]): String or Path
|
|
259
|
-
object of directory path
|
|
260
|
-
filename (str): filename to search for
|
|
261
|
-
including wildcard pattern
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
Path: Path object of the root problem folder
|
|
265
|
-
"""
|
|
266
|
-
|
|
267
|
-
def _check_file_match_folder(path: Path, filename: str) -> bool:
|
|
268
|
-
""" Check folder is same name as the filename given
|
|
269
|
-
filename including wildcard.
|
|
270
|
-
|
|
271
|
-
Args:
|
|
272
|
-
path (Path): Path object of directory path
|
|
273
|
-
filename (str): filename to search for
|
|
274
|
-
including wildcard pattern
|
|
275
|
-
Returns:
|
|
276
|
-
bool: True if path exists, False otherwise
|
|
277
|
-
"""
|
|
278
|
-
for file in path.glob(filename):
|
|
279
|
-
name, ext = os.path.splitext(file.name)
|
|
280
|
-
folder_name = path.parts[-1]
|
|
281
|
-
# print(f'{name} {folder_name=} {name=} {ext=}')
|
|
282
|
-
if name == folder_name:
|
|
283
|
-
return True
|
|
284
|
-
# read yaml file
|
|
285
|
-
if ext == '.yaml':
|
|
286
|
-
with open(file, 'r', encoding='utf-8') as f:
|
|
287
|
-
data = yaml.safe_load(f)
|
|
288
|
-
if 'problemid' in data:
|
|
289
|
-
return True
|
|
290
|
-
return False
|
|
291
|
-
|
|
292
|
-
# print(f'{cur_dir_path=} {filename=}')
|
|
293
|
-
if not filename:
|
|
294
|
-
filename = '.yaml'
|
|
295
|
-
cur_path = Path(cur_dir_path)
|
|
296
|
-
if _check_file_match_folder(cur_path, filename):
|
|
297
|
-
return cur_path
|
|
298
|
-
for parent in cur_path.parents:
|
|
299
|
-
# print('parent', parent, file=sys.stderr)
|
|
300
|
-
if _check_file_match_folder(parent, filename):
|
|
301
|
-
return parent
|
|
302
|
-
raise FileNotFoundError("Error: Problem root folder not found.")
|
|
303
|
-
|
|
304
|
-
|
|
305
251
|
# flake8: noqa: C901
|
|
306
252
|
def update_args(problemid: str,
|
|
307
253
|
loc_language: str,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
""" Utility functions. """
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
from math import inf
|
|
6
|
+
from typing import Union
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_problem_root_folder(
|
|
11
|
+
cur_dir_path: Union[str, Path],
|
|
12
|
+
filename: str
|
|
13
|
+
) -> Path:
|
|
14
|
+
"""Find the root problem folder given a directory path and filename.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
cur_dir_path (Union[str, Path]): String or Path
|
|
18
|
+
object of directory path
|
|
19
|
+
filename (str): filename to search for
|
|
20
|
+
including wildcard pattern
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Path: Path object of the root problem folder
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def _check_file_match_folder(path: Path, filename: str) -> bool:
|
|
27
|
+
""" Check folder is same name as the filename given
|
|
28
|
+
filename including wildcard.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
path (Path): Path object of directory path
|
|
32
|
+
filename (str): filename to search for
|
|
33
|
+
including wildcard pattern
|
|
34
|
+
Returns:
|
|
35
|
+
bool: True if path exists, False otherwise
|
|
36
|
+
"""
|
|
37
|
+
for file in path.glob(filename):
|
|
38
|
+
name, ext = os.path.splitext(file.name)
|
|
39
|
+
folder_name = path.parts[-1]
|
|
40
|
+
# print(f'{name} {folder_name=} {name=} {ext=}')
|
|
41
|
+
if name == folder_name:
|
|
42
|
+
return True
|
|
43
|
+
# read yaml file
|
|
44
|
+
if ext == '.yaml':
|
|
45
|
+
with open(file, 'r', encoding='utf-8') as f:
|
|
46
|
+
data = yaml.safe_load(f)
|
|
47
|
+
if 'problemid' in data:
|
|
48
|
+
return True
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
# print(f'{cur_dir_path=} {filename=}')
|
|
52
|
+
if not filename:
|
|
53
|
+
filename = '.yaml'
|
|
54
|
+
cur_path = Path(cur_dir_path)
|
|
55
|
+
if _check_file_match_folder(cur_path, filename):
|
|
56
|
+
return cur_path
|
|
57
|
+
for parent in cur_path.parents:
|
|
58
|
+
# print('parent', parent, file=sys.stderr)
|
|
59
|
+
if _check_file_match_folder(parent, filename):
|
|
60
|
+
return parent
|
|
61
|
+
raise FileNotFoundError("Error: Problem root folder not found.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def check_answer(expected: str, ans: str, places: float = inf) -> bool:
|
|
65
|
+
"""Compare two numeric strings with given precision.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
expected (str): expected result
|
|
69
|
+
ans (str): actual result
|
|
70
|
+
places (float): decimal places for approximation;
|
|
71
|
+
default inf for string comparison
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
bool: True if the two values are equal, False otherwise
|
|
75
|
+
"""
|
|
76
|
+
expect_ans = expected.strip().split('\n')
|
|
77
|
+
actual_ans = ans.strip().split('\n')
|
|
78
|
+
if len(expect_ans) != len(actual_ans):
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
if places == inf:
|
|
82
|
+
if expect_ans == actual_ans:
|
|
83
|
+
return True
|
|
84
|
+
else:
|
|
85
|
+
return False
|
|
86
|
+
try:
|
|
87
|
+
for i, ex_ans in enumerate(expect_ans):
|
|
88
|
+
flt_expected = float(ex_ans)
|
|
89
|
+
flt_ans = float(actual_ans[i])
|
|
90
|
+
if abs(flt_expected - flt_ans) > 10**(-places):
|
|
91
|
+
return False
|
|
92
|
+
return True
|
|
93
|
+
except ValueError:
|
|
94
|
+
return False
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "kattis-cli"
|
|
3
|
-
version = "1.0.
|
|
4
|
-
authors = ["Ram Basnet <
|
|
3
|
+
version = "1.0.5"
|
|
4
|
+
authors = ["Ram Basnet <rbasnet@coloradomesa.edu>"]
|
|
5
5
|
description = "A command-line tool for Kattis"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
repository = "https://github.com/rambasnet/kattis-cli"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|