kattis-cli 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

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/download.py CHANGED
@@ -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
 
kattis_cli/kattis.py CHANGED
@@ -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 utility
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 utility.GUESS_MAINFILE:
185
+ if language in languages.GUESS_MAINFILE:
186
186
  console.print('Main file:', mainclass)
187
187
  else:
188
188
  console.print('Mainclass:', mainclass)
kattis_cli/main.py CHANGED
@@ -1,5 +1,13 @@
1
- """ Main module for the kattis_cli package."""
2
- __version__ = '1.0.2'
1
+ """ Main module for the kattis_cli package.
2
+ This is the main.py file for the kattis_cli package.
3
+
4
+ Change the contents here instead of main.py.
5
+ build.sh script copies the contents of this file to main.py.
6
+
7
+ Change the __version__ to match in pyproject.toml
8
+ Has to be higher than the pypi version.
9
+ """
10
+ __version__ = '1.0.4'
3
11
 
4
12
  from math import inf
5
13
  from typing import Tuple
@@ -10,15 +18,17 @@ import kattis_cli.download as download
10
18
  import kattis_cli.ui as ui
11
19
  import kattis_cli.test_solution as test_solution
12
20
  import kattis_cli.kattis as kattis
13
- import kattis_cli.utils.utility as utility
21
+ import kattis_cli.utils.languages as languages
14
22
  import kattis_cli.kattis_setup as kattis_setup
15
23
 
16
24
 
17
25
  @click.group()
26
+ @click.version_option(version=__version__, prog_name='kattis-cli')
18
27
  def main() -> None:
19
28
  """
20
29
  CLI for downloading, testing and submitting Kattis problems.
21
30
  """
31
+ pass
22
32
 
23
33
 
24
34
  @main.command(help='Download sample data & metadata.')
@@ -34,7 +44,7 @@ def get(problemid: str) -> None:
34
44
  except requests.exceptions.InvalidURL:
35
45
  console.print(
36
46
  f"""Sample data for Problem ID: [bold blue]
37
- {problemid}[/bold blue] not found.")
47
+ {problemid}[/bold blue] not found.")
38
48
  """)
39
49
  console.print(
40
50
  f"Downloading metadata: [bold blue]{problemid}[/bold blue]")
@@ -65,11 +75,11 @@ def test(
65
75
  """Test solution with sample files.
66
76
  """
67
77
  problemid, loc_language, mainclass, _files, root_folder, lang_config = \
68
- utility.update_args(problemid, language, mainclass, list(files))
78
+ languages.update_args(problemid, language, mainclass, list(files))
69
79
  # print('After - ', f'{problemid=} {language=} {mainclass=} {_files=}')
70
80
  # lang_config = config.parse_config(language)
71
81
  if not mainclass:
72
- mainclass = utility.guess_mainfile(
82
+ mainclass = languages.guess_mainfile(
73
83
  language, _files, problemid, lang_config)
74
84
 
75
85
  test_solution.test_samples(
@@ -101,14 +111,14 @@ def submit(problemid: str, language: str,
101
111
  """Submit a solution to Kattis.
102
112
  """
103
113
  problemid, language, mainclass, _files, _, lang_config = \
104
- utility.update_args(
114
+ languages.update_args(
105
115
  problemid, language, mainclass, list(files))
106
116
  # Finally, submit the solution
107
117
  # print(f'{problemid=} {language=} {mainclass=} {tag=} {force=} {_files=}')
108
118
  if not mainclass:
109
- mainclass = utility.guess_mainfile(
119
+ mainclass = languages.guess_mainfile(
110
120
  language, _files, problemid, lang_config)
111
- kat_lang = utility.LOCAL_TO_KATTIS[language]
121
+ kat_lang = languages.LOCAL_TO_KATTIS[language]
112
122
  kattis.submit_solution(_files, problemid,
113
123
  kat_lang, mainclass,
114
124
  tag, force)
@@ -1,7 +1,6 @@
1
1
  """Tester module for Kattis.
2
2
  """
3
3
 
4
- from math import inf
5
4
  from typing import Any, List, Dict
6
5
  import glob
7
6
  import time
@@ -17,26 +16,7 @@ from rich.prompt import Confirm
17
16
  from rich.markup import escape
18
17
 
19
18
  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
19
+ from kattis_cli.utils import languages, run_program, utility
40
20
 
41
21
 
42
22
  def test_samples(
@@ -46,7 +26,7 @@ def test_samples(
46
26
  problem_root_folder: str,
47
27
  files: List[str],
48
28
  lang_config: Dict[Any, Any],
49
- accuracy: float = inf
29
+ accuracy: float = 0
50
30
  ) -> None:
51
31
  """Tests a problem by running all the .in files in
52
32
  the problem folder and comparing the output to the .ans files.
@@ -147,14 +127,15 @@ def test_samples(
147
127
  if code != 0:
148
128
  ans = error
149
129
  # console.print(f"{ans=} {error=}")
150
- if accuracy == inf: # string comparison
130
+ if accuracy == 0: # string comparison
151
131
  if expected == ans.encode('utf-8').strip():
152
132
  result = "[bold green]✅[/bold green]"
153
133
  count += 1
154
134
  else:
155
135
  result = "[bold red]❌[/bold red]"
156
136
  else: # floating point comparison
157
- if compare_floats(expected.decode('utf-8'), ans, accuracy):
137
+ if utility.compare_floats(expected.decode('utf-8'),
138
+ ans, accuracy):
158
139
  result = "[bold green]✅[/bold green]"
159
140
  count += 1
160
141
  else:
@@ -188,7 +169,7 @@ def test_samples(
188
169
  "Awesome... Time to submit it to :cat: Kattis! :cat:",
189
170
  style="bold green")
190
171
  if Confirm.ask("Submit to Kattis?", default=True):
191
- kat_language = utility.LOCAL_TO_KATTIS.get(loc_language, '')
172
+ kat_language = languages.LOCAL_TO_KATTIS.get(loc_language, '')
192
173
  kattis.submit_solution(files, problemid,
193
174
  kat_language, mainclass,
194
175
  tag="", force=True)
@@ -9,7 +9,7 @@ import configparser
9
9
  from tomlkit import load
10
10
  import yaml
11
11
 
12
- from .utility import find_problem_root_folder
12
+ from kattis_cli.utils.utility import find_problem_root_folder
13
13
 
14
14
  _DEFAULT_CONFIG = Path.home().joinpath('.kattisrc')
15
15
 
@@ -0,0 +1,332 @@
1
+ """Module for language utilities.
2
+ """
3
+
4
+ from typing import List, Dict, Any
5
+ import sys
6
+ import os
7
+ import re
8
+ from pathlib import Path
9
+ from rich.console import Console
10
+ from kattis_cli.utils import config
11
+ from kattis_cli.utils.utility import find_problem_root_folder
12
+
13
+
14
+ LANGUAGE_GUESS = {
15
+ '.c': 'c',
16
+ '.c++': 'cpp',
17
+ '.cc': 'cpp',
18
+ '.c#': 'csharp',
19
+ '.cpp': 'cpp',
20
+ '.cs': 'csharp',
21
+ '.cxx': 'cpp',
22
+ '.cbl': 'cobol',
23
+ '.cob': 'cobol',
24
+ '.cpy': 'cobol',
25
+ '.fs': 'fsharp',
26
+ '.go': 'go',
27
+ '.hs': 'haskell',
28
+ '.java': 'java',
29
+ '.js': 'nodejs',
30
+ '.ts': 'typescript',
31
+ '.kt': 'kotlin',
32
+ '.lisp': 'lisp',
33
+ '.cl': 'lisp',
34
+ '.m': 'objective-c',
35
+ '.ml': 'ocaml',
36
+ '.pas': 'pascal',
37
+ '.php': 'php',
38
+ '.pl': 'prolog',
39
+ '.py': 'python3',
40
+ '.rb': 'ruby',
41
+ '.rs': 'rust',
42
+ '.scala': 'scala',
43
+ '.f90': 'fortran',
44
+ '.f': 'fortran',
45
+ '.for': 'fortran',
46
+ '.sh': 'bash',
47
+ '.apl': 'apl',
48
+ '.ss': 'gerbil',
49
+ '.jl': 'julia',
50
+ '.vb': 'vb',
51
+ '.dart': 'dart',
52
+ '.zig': 'zig',
53
+ '.swift': 'swift',
54
+ '.nim': 'nim',
55
+ }
56
+
57
+ GUESS_MAINCLASS = {'Java', 'Kotlin', 'Scala'}
58
+
59
+ GUESS_MAINFILE = {
60
+ 'APL',
61
+ 'Bash',
62
+ 'Dart',
63
+ 'Gerbil',
64
+ 'JavaScript (Node.js)',
65
+ 'Julia',
66
+ 'Common Lisp',
67
+ 'Pascal',
68
+ 'PHP',
69
+ 'Python 3',
70
+ 'Ruby',
71
+ 'Rust',
72
+ 'TypeScript',
73
+ 'Zig'
74
+ }
75
+
76
+ # mapping is used for .kattis-cli.toml file configuration
77
+ LOCAL_TO_KATTIS = {
78
+ 'python3': 'Python 3',
79
+ 'java': 'Java',
80
+ 'cpp': 'C++',
81
+ 'c++': 'C++',
82
+ 'nodejs': 'JavaScript (Node.js)',
83
+ 'typescript': 'TypeScript',
84
+ 'csharp': 'C#',
85
+ 'kotlin': 'Kotlin',
86
+ 'scala': 'Scala',
87
+ 'rust': 'Rust',
88
+ 'pascal': 'Pascal',
89
+ 'go': 'Go',
90
+ 'haskell': 'Haskell',
91
+ 'ruby': 'Ruby',
92
+ 'php': 'PHP',
93
+ 'lisp': 'Common Lisp',
94
+ 'fortran': 'Fortran',
95
+ 'bash': 'Bash',
96
+ 'apl': 'APL',
97
+ 'gerbil': 'Gerbil',
98
+ 'julia': 'Julia',
99
+ 'vb': 'Visual Basic',
100
+ 'dart': 'Dart',
101
+ 'zig': 'Zig',
102
+ 'swift': 'Swift',
103
+ 'nim': 'Nim',
104
+ 'ocaml': 'OCaml',
105
+ 'fsharp': 'F#',
106
+ 'cobol': 'COBOL',
107
+ 'prolog': 'Prolog',
108
+ 'objective-c': 'Objective-C',
109
+ 'c': 'C',
110
+ 'c#': 'C#'
111
+ }
112
+
113
+
114
+ def guess_language(ext: str, files: List[str]) -> str:
115
+ """Guess the language.
116
+
117
+ Args:
118
+ ext (str): File extension.
119
+ files (List[str]): Tuple of files.
120
+
121
+ Returns:
122
+ str: guessed language
123
+ """
124
+ if ext == ".C":
125
+ return "cpp"
126
+ ext = ext.lower()
127
+ if ext == ".h":
128
+ if any(f.endswith(".c") for f in files):
129
+ return "c"
130
+ else:
131
+ return "cpp"
132
+ if ext == ".py":
133
+ return "python3"
134
+ return LANGUAGE_GUESS.get(ext, '')
135
+
136
+
137
+ # flake8: noqa: C901
138
+ def guess_mainfile(
139
+ kat_language: str,
140
+ files: List[str],
141
+ problemid: str,
142
+ lang_config: Dict[Any, Any]) -> Any:
143
+ """Guess the main file.
144
+
145
+ Args:
146
+ kat_language (str): programming language name used by Kattis
147
+ files (List[str]): Tuple of files
148
+
149
+ Returns:
150
+ str: main file
151
+ """
152
+ if len(files) == 1:
153
+ return files[0]
154
+ # check .kattis-cli.toml file
155
+ if 'mainfile' in lang_config:
156
+ return lang_config['mainfile'].replace(('{problemid}'), problemid)
157
+ for filename in files:
158
+ if os.path.splitext(os.path.basename(filename))[0] in ['main', 'Main']:
159
+ return filename
160
+ if problemid and os.path.splitext(
161
+ os.path.basename(filename))[0] == problemid:
162
+ return filename
163
+ for filename in files:
164
+ try:
165
+ with open(filename, 'r', encoding='utf-8') as f:
166
+ conts = f.read()
167
+ if kat_language in [
168
+ 'Java', 'Rust', 'Scala', 'Kotlin'] and re.search(
169
+ r' main\s*\(', conts):
170
+ return filename
171
+ if kat_language == 'Pascal' and re.match(
172
+ r'^\s*[Pp]rogram\b', conts):
173
+ return filename
174
+ except IOError:
175
+ pass
176
+ # main file is the one with problemid
177
+ return files[0]
178
+
179
+
180
+ def guess_mainclass(
181
+ problemid: str,
182
+ kat_language: str,
183
+ files: List[str],
184
+ lang_config: Any) -> Any:
185
+ """Guess the main class.
186
+
187
+ Args:
188
+ kat_language (str): programming language name used by Kattis
189
+ files (List[str]): List of files to guess mainclass from
190
+
191
+ Returns:
192
+ str: mainclass name or empty string
193
+ """
194
+ if kat_language in GUESS_MAINFILE and len(files) > 1:
195
+ return os.path.basename(
196
+ guess_mainfile(
197
+ kat_language,
198
+ files,
199
+ problemid,
200
+ lang_config))
201
+ if kat_language in GUESS_MAINCLASS:
202
+ mainfile = os.path.basename(
203
+ guess_mainfile(
204
+ kat_language,
205
+ files,
206
+ problemid,
207
+ lang_config))
208
+ name = os.path.splitext(mainfile)[0]
209
+ if kat_language == 'Kotlin':
210
+ return name[0].upper() + name[1:] + 'Kt'
211
+ return name
212
+ return ''
213
+
214
+
215
+ def validate_language(loc_language: str) -> bool:
216
+ """Check if valid language.
217
+
218
+ Args:
219
+ loc_language (str): programming language provided by user
220
+
221
+ Returns:
222
+ bool: True if valid language, exit otherwise.
223
+ """
224
+ if loc_language in LOCAL_TO_KATTIS:
225
+ return True
226
+ console = Console()
227
+ console.print(f'Invalid language: "{loc_language}"', style='bold red')
228
+ console.print('Valid languages are:', style='bold green')
229
+ for lang in sorted(LOCAL_TO_KATTIS.keys()):
230
+ console.print(f'\t\t - {lang}')
231
+ exit(1)
232
+
233
+
234
+ def valid_extension(file: str) -> bool:
235
+ """Check if valid extension.
236
+
237
+ Args:
238
+ file (str): File name.
239
+
240
+ Returns:
241
+ bool: True if valid extension, False otherwise.
242
+ """
243
+ if os.path.isfile(file):
244
+ ext = os.path.splitext(file)
245
+ if len(ext) != 2:
246
+ return False
247
+ return ext[1] in LANGUAGE_GUESS
248
+ return False
249
+
250
+
251
+ # flake8: noqa: C901
252
+ def update_args(problemid: str,
253
+ loc_language: str,
254
+ mainclass: str,
255
+ files: List[str]) -> Any:
256
+ """Check if problemid, language, mainclass, and program files are valid.
257
+
258
+ Args:
259
+ problemid (str): problemid
260
+ loc_language (str): programming language provided by user
261
+ mainclass (str): main class
262
+ files (List[str]): List of files
263
+
264
+ Returns:
265
+ Tuple[str]: Update problemid, kattis_language, mainclass, and files
266
+ """
267
+
268
+ console = Console()
269
+ if not files:
270
+ files = get_coding_files()
271
+ # check if problemid is given
272
+ # if not problemid:
273
+ cur_folder = Path.cwd()
274
+ root_folder = Path.cwd()
275
+ if not problemid:
276
+ for f in files:
277
+ try:
278
+ root_folder = find_problem_root_folder(
279
+ cur_folder, f.lower())
280
+ # if not problemid:
281
+ problemid = root_folder.name
282
+ break
283
+ except FileNotFoundError:
284
+ # print(ex)
285
+ pass
286
+ if not problemid:
287
+ try:
288
+ root_folder = find_problem_root_folder(
289
+ cur_folder, '*.yaml')
290
+ problemid = root_folder.name
291
+ except FileNotFoundError:
292
+ console.print(f'''No problemid specified and I failed to guess
293
+ problemid and root problem folder from filename(s) and cwd: {cur_folder}.''',
294
+ style='bold red')
295
+ sys.exit(1)
296
+ # check if language
297
+ if not loc_language:
298
+ _, ext = os.path.splitext(os.path.basename(files[0]))
299
+ # Guess language from files
300
+ loc_language = guess_language(ext, files)
301
+ if not loc_language:
302
+ console.print(f'''\
303
+ No language specified, and I failed to guess language from
304
+ filename extension "{ext}"''')
305
+ sys.exit(1)
306
+ # check if valid language
307
+ validate_language(loc_language)
308
+ lang_config = config.parse_config(loc_language)
309
+ kat_language = LOCAL_TO_KATTIS[loc_language]
310
+ if not mainclass:
311
+ mainclass = guess_mainclass(
312
+ problemid, kat_language, files, lang_config)
313
+ # print(f'Returning...{problemid=} {language=} {mainclass=} {files=}')
314
+ return problemid, loc_language, mainclass, files, root_folder, lang_config
315
+
316
+
317
+ def get_coding_files() -> List[str]:
318
+ """Get coding files from current directory.
319
+
320
+ Returns:
321
+ List[str]: List of coding files.
322
+ """
323
+ cur_folder = str(Path.cwd())
324
+ console = Console()
325
+ files = [
326
+ f for f in os.listdir(cur_folder) if valid_extension(f)]
327
+ if not files:
328
+ console.print(
329
+ 'No source file(s) found in the current folder!',
330
+ style='bold red')
331
+ exit(1)
332
+ return files
@@ -1,251 +1,9 @@
1
- """Module for language utilities.
2
- """
1
+ """ Utility functions. """
3
2
 
4
- from typing import List, Union, Any, Dict
5
- import sys
6
- import os
7
- import re
8
3
  from pathlib import Path
4
+ import os
5
+ from typing import Union
9
6
  import yaml
10
- from rich.console import Console
11
- from kattis_cli.utils import config
12
-
13
-
14
- LANGUAGE_GUESS = {
15
- '.c': 'c',
16
- '.c++': 'cpp',
17
- '.cc': 'cpp',
18
- '.c#': 'csharp',
19
- '.cpp': 'cpp',
20
- '.cs': 'csharp',
21
- '.cxx': 'cpp',
22
- '.cbl': 'cobol',
23
- '.cob': 'cobol',
24
- '.cpy': 'cobol',
25
- '.fs': 'fsharp',
26
- '.go': 'go',
27
- '.hs': 'haskell',
28
- '.java': 'java',
29
- '.js': 'nodejs',
30
- '.ts': 'typescript',
31
- '.kt': 'kotlin',
32
- '.lisp': 'lisp',
33
- '.cl': 'lisp',
34
- '.m': 'objective-c',
35
- '.ml': 'ocaml',
36
- '.pas': 'pascal',
37
- '.php': 'php',
38
- '.pl': 'prolog',
39
- '.py': 'python3',
40
- '.rb': 'ruby',
41
- '.rs': 'rust',
42
- '.scala': 'scala',
43
- '.f90': 'fortran',
44
- '.f': 'fortran',
45
- '.for': 'fortran',
46
- '.sh': 'bash',
47
- '.apl': 'apl',
48
- '.ss': 'gerbil',
49
- '.jl': 'julia',
50
- '.vb': 'vb',
51
- '.dart': 'dart',
52
- '.zig': 'zig',
53
- '.swift': 'swift',
54
- '.nim': 'nim',
55
- }
56
-
57
- GUESS_MAINCLASS = {'Java', 'Kotlin', 'Scala'}
58
-
59
- GUESS_MAINFILE = {
60
- 'APL',
61
- 'Bash',
62
- 'Dart',
63
- 'Gerbil',
64
- 'JavaScript (Node.js)',
65
- 'Julia',
66
- 'Common Lisp',
67
- 'Pascal',
68
- 'PHP',
69
- 'Python 3',
70
- 'Ruby',
71
- 'Rust',
72
- 'TypeScript',
73
- 'Zig'
74
- }
75
-
76
- # mapping is used for .kattis-cli.toml file configuration
77
- LOCAL_TO_KATTIS = {
78
- 'python3': 'Python 3',
79
- 'java': 'Java',
80
- 'cpp': 'C++',
81
- 'c++': 'C++',
82
- 'nodejs': 'JavaScript (Node.js)',
83
- 'typescript': 'TypeScript',
84
- 'csharp': 'C#',
85
- 'kotlin': 'Kotlin',
86
- 'scala': 'Scala',
87
- 'rust': 'Rust',
88
- 'pascal': 'Pascal',
89
- 'go': 'Go',
90
- 'haskell': 'Haskell',
91
- 'ruby': 'Ruby',
92
- 'php': 'PHP',
93
- 'lisp': 'Common Lisp',
94
- 'fortran': 'Fortran',
95
- 'bash': 'Bash',
96
- 'apl': 'APL',
97
- 'gerbil': 'Gerbil',
98
- 'julia': 'Julia',
99
- 'vb': 'Visual Basic',
100
- 'dart': 'Dart',
101
- 'zig': 'Zig',
102
- 'swift': 'Swift',
103
- 'nim': 'Nim',
104
- 'ocaml': 'OCaml',
105
- 'fsharp': 'F#',
106
- 'cobol': 'COBOL',
107
- 'prolog': 'Prolog',
108
- 'objective-c': 'Objective-C',
109
- 'c': 'C',
110
- 'c#': 'C#'
111
- }
112
-
113
-
114
- def guess_language(ext: str, files: List[str]) -> str:
115
- """Guess the language.
116
-
117
- Args:
118
- ext (str): File extension.
119
- files (List[str]): Tuple of files.
120
-
121
- Returns:
122
- str: guessed language
123
- """
124
- if ext == ".C":
125
- return "cpp"
126
- ext = ext.lower()
127
- if ext == ".h":
128
- if any(f.endswith(".c") for f in files):
129
- return "c"
130
- else:
131
- return "cpp"
132
- if ext == ".py":
133
- return "python3"
134
- return LANGUAGE_GUESS.get(ext, '')
135
-
136
-
137
- # flake8: noqa: C901
138
- def guess_mainfile(
139
- kat_language: str,
140
- files: List[str],
141
- problemid: str,
142
- lang_config: Dict[Any, Any]) -> Any:
143
- """Guess the main file.
144
-
145
- Args:
146
- kat_language (str): programming language name used by Kattis
147
- files (List[str]): Tuple of files
148
-
149
- Returns:
150
- str: main file
151
- """
152
- if len(files) == 1:
153
- return files[0]
154
- # check .kattis-cli.toml file
155
- if 'mainfile' in lang_config:
156
- return lang_config['mainfile'].replace(('{problemid}'), problemid)
157
- for filename in files:
158
- if os.path.splitext(os.path.basename(filename))[0] in ['main', 'Main']:
159
- return filename
160
- if problemid and os.path.splitext(
161
- os.path.basename(filename))[0] == problemid:
162
- return filename
163
- for filename in files:
164
- try:
165
- with open(filename, 'r', encoding='utf-8') as f:
166
- conts = f.read()
167
- if kat_language in [
168
- 'Java', 'Rust', 'Scala', 'Kotlin'] and re.search(
169
- r' main\s*\(', conts):
170
- return filename
171
- if kat_language == 'Pascal' and re.match(
172
- r'^\s*[Pp]rogram\b', conts):
173
- return filename
174
- except IOError:
175
- pass
176
- # main file is the one with problemid
177
- return files[0]
178
-
179
-
180
- def guess_mainclass(
181
- problemid: str,
182
- kat_language: str,
183
- files: List[str],
184
- lang_config: Any) -> Any:
185
- """Guess the main class.
186
-
187
- Args:
188
- kat_language (str): programming language name used by Kattis
189
- files (List[str]): List of files to guess mainclass from
190
-
191
- Returns:
192
- str: mainclass name or empty string
193
- """
194
- if kat_language in GUESS_MAINFILE and len(files) > 1:
195
- return os.path.basename(
196
- guess_mainfile(
197
- kat_language,
198
- files,
199
- problemid,
200
- lang_config))
201
- if kat_language in GUESS_MAINCLASS:
202
- mainfile = os.path.basename(
203
- guess_mainfile(
204
- kat_language,
205
- files,
206
- problemid,
207
- lang_config))
208
- name = os.path.splitext(mainfile)[0]
209
- if kat_language == 'Kotlin':
210
- return name[0].upper() + name[1:] + 'Kt'
211
- return name
212
- return ''
213
-
214
-
215
- def validate_language(loc_language: str) -> bool:
216
- """Check if valid language.
217
-
218
- Args:
219
- loc_language (str): programming language provided by user
220
-
221
- Returns:
222
- bool: True if valid language, exit otherwise.
223
- """
224
- if loc_language in LOCAL_TO_KATTIS:
225
- return True
226
- console = Console()
227
- console.print(f'Invalid language: "{loc_language}"', style='bold red')
228
- console.print('Valid languages are:', style='bold green')
229
- for lang in sorted(LOCAL_TO_KATTIS.keys()):
230
- console.print(f'\t\t - {lang}')
231
- exit(1)
232
-
233
-
234
- def valid_extension(file: str) -> bool:
235
- """Check if valid extension.
236
-
237
- Args:
238
- file (str): File name.
239
-
240
- Returns:
241
- bool: True if valid extension, False otherwise.
242
- """
243
- if os.path.isfile(file):
244
- ext = os.path.splitext(file)
245
- if len(ext) != 2:
246
- return False
247
- return ext[1] in LANGUAGE_GUESS
248
- return False
249
7
 
250
8
 
251
9
  def find_problem_root_folder(
@@ -302,85 +60,27 @@ def find_problem_root_folder(
302
60
  raise FileNotFoundError("Error: Problem root folder not found.")
303
61
 
304
62
 
305
- # flake8: noqa: C901
306
- def update_args(problemid: str,
307
- loc_language: str,
308
- mainclass: str,
309
- files: List[str]) -> Any:
310
- """Check if problemid, language, mainclass, and program files are valid.
63
+ def compare_floats(expected: str, ans: str, places: float) -> bool:
64
+ """Compare two floating point numbers with given accuracy.
311
65
 
312
66
  Args:
313
- problemid (str): problemid
314
- loc_language (str): programming language provided by user
315
- mainclass (str): main class
316
- files (List[str]): List of files
67
+ expected (str): expected result
68
+ ans (str): actual result
69
+ places (float): decimal places for approximation
317
70
 
318
71
  Returns:
319
- Tuple[str]: Update problemid, kattis_language, mainclass, and files
72
+ bool: True if the two numbers are equal within the given accuracy
320
73
  """
321
-
322
- console = Console()
323
- if not files:
324
- files = get_coding_files()
325
- # check if problemid is given
326
- # if not problemid:
327
- cur_folder = Path.cwd()
328
- root_folder = Path.cwd()
329
- if not problemid:
330
- for f in files:
331
- try:
332
- root_folder = find_problem_root_folder(
333
- cur_folder, f.lower())
334
- # if not problemid:
335
- problemid = root_folder.name
336
- break
337
- except FileNotFoundError:
338
- # print(ex)
339
- pass
340
- if not problemid:
341
- try:
342
- root_folder = find_problem_root_folder(
343
- cur_folder, '*.yaml')
344
- problemid = root_folder.name
345
- except FileNotFoundError:
346
- console.print(f'''No problemid specified and I failed to guess
347
- problemid and root problem folder from filename(s) and cwd: {cur_folder}.''',
348
- style='bold red')
349
- sys.exit(1)
350
- # check if language
351
- if not loc_language:
352
- _, ext = os.path.splitext(os.path.basename(files[0]))
353
- # Guess language from files
354
- loc_language = guess_language(ext, files)
355
- if not loc_language:
356
- console.print(f'''\
357
- No language specified, and I failed to guess language from
358
- filename extension "{ext}"''')
359
- sys.exit(1)
360
- # check if valid language
361
- validate_language(loc_language)
362
- lang_config = config.parse_config(loc_language)
363
- kat_language = LOCAL_TO_KATTIS[loc_language]
364
- if not mainclass:
365
- mainclass = guess_mainclass(
366
- problemid, kat_language, files, lang_config)
367
- # print(f'Returning...{problemid=} {language=} {mainclass=} {files=}')
368
- return problemid, loc_language, mainclass, files, root_folder, lang_config
369
-
370
-
371
- def get_coding_files() -> List[str]:
372
- """Get coding files from current directory.
373
-
374
- Returns:
375
- List[str]: List of coding files.
376
- """
377
- cur_folder = str(Path.cwd())
378
- console = Console()
379
- files = [
380
- f for f in os.listdir(cur_folder) if valid_extension(f)]
381
- if not files:
382
- console.print(
383
- 'No source file(s) found in the current folder!',
384
- style='bold red')
385
- exit(1)
386
- return files
74
+ expect_ans = expected.strip().split('\n')
75
+ actual_ans = ans.strip().split('\n')
76
+ if len(expect_ans) != len(actual_ans):
77
+ return False
78
+ try:
79
+ for i, ex_ans in enumerate(expect_ans):
80
+ flt_expected = float(ex_ans)
81
+ flt_ans = float(actual_ans[i])
82
+ if abs(flt_expected - flt_ans) > 10**(-places):
83
+ return False
84
+ return True
85
+ except ValueError:
86
+ return False
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kattis-cli
3
- Version: 1.0.2
3
+ Version: 1.0.4
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: rambasnet@gmail.com
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
@@ -75,21 +75,25 @@ If you've Python version 3.8 or higher, you can skip creating virtual environmen
75
75
  ```bash
76
76
  pip install kattis-cli
77
77
  python -m pip install kattis-cli
78
+ kattis --version
78
79
  ```
79
80
 
80
81
  - on Windows add the path shown in the output of the above command to your PATH environment variable
81
82
 
82
-
83
- ## Update Kattis-CLI
83
+ ## Update/Upgrade Kattis-CLI
84
84
 
85
85
  - remove or rename **.kattis-cli.toml** file in your home directory
86
86
  - activate virtual environment if you've created one for kattis-cli
87
87
 
88
88
  ```bash
89
+ kattis --version
89
90
  pip install kattis-cli --upgrade
90
91
  python -m pip install kattis-cli --upgrade
91
92
  ```
92
93
 
94
+ - on Windows add the path shown in the output of the above command to your PATH environment variable
95
+
96
+
93
97
  ## Kattis configuration
94
98
 
95
99
  - run the following command and enter your Kattis credentials
@@ -113,13 +117,13 @@ kattis --help
113
117
  - problem id can be found in the last part of the URL of the problem
114
118
  - example: [https://open.kattis.com/problems/cold](https://open.kattis.com/problems/cold) => problem id: **cold**
115
119
 
116
- ![Problem id](images/problemid.png)
120
+ ![Problem id](./images/problemid.png)
117
121
 
118
122
  ```bash
119
123
  kattis get <problem_id>
120
124
  ```
121
125
 
122
- ![Get problem id from URL](images/kattis_get.png)
126
+ ![Get problem id from URL](./images/kattis_get.png)
123
127
 
124
128
  ### Display problem metadata
125
129
 
@@ -128,10 +132,12 @@ cd <problem_id>
128
132
  kattis info
129
133
  ```
130
134
 
131
- ![Problem info](images/kattis_info.png)
135
+ ![Problem info](./images/kattis_info.png)
132
136
 
133
137
  ### Test a solution locally
134
138
 
139
+ ![Test](images/kattis_test.png)
140
+
135
141
  - currently the following languages have been tested: Python 3, C++, NodeJS, C, Java
136
142
  - make sure CLI compilers are in your PATH
137
143
  - make sure python3 files have first line shebang: !/usr/bin/env python3
@@ -141,10 +147,20 @@ kattis info
141
147
 
142
148
  ```bash
143
149
  cd <problem_id>
144
- kattis test
150
+ kattis test # for exact comparion of answers (string and int)
151
+ kattis test -a 6 # Answer accepted upto 6 decimal places of accuracy
152
+ ```
153
+
154
+ ### Testing floating point results
155
+
156
+ - for floating point ouput, problem provides the tolerance or accuracy upto certain decimal points
157
+ - one can use `-a <N>` switch after kattis test command to provide the decimal places of accuracy
158
+ - e.g., the following command checks for accuracy upto 6 decimal points or absolute error upto $10^-6$
159
+
160
+ ```bash
161
+ kattis test -a 6
145
162
  ```
146
163
 
147
- ![Test](images/kattis_test.png)
148
164
 
149
165
  ### Submit a problem
150
166
 
@@ -0,0 +1,19 @@
1
+ kattis_cli/.kattis-cli.toml,sha256=kGh0gmpFAnivyUC9hR8Y1ZqYJeTBWNyBQ9zOg4AInZ0,898
2
+ kattis_cli/__init__.py,sha256=afN92pog2fGyicY6KNBofYbCBYbYj4Fpi_INUSpsc-E,402
3
+ kattis_cli/download.py,sha256=kvUJdqRW3_ETvTd4XMruEs87RWPVHp_3a4VUhcYZEz8,5863
4
+ kattis_cli/kattis.py,sha256=uZct7vjbEW2HA5FJr1ItSZ-b07P9JQHv5SccV4Pqjo8,13771
5
+ kattis_cli/kattis_setup.py,sha256=rq_-Fz8oafxFJ8MOcxJqHQT8-bezgVHZXY2hm4EQF3U,4357
6
+ kattis_cli/main.py,sha256=aI9EWMqAr-QawCzDUPJSpxwoIsEHSi2ed-23-KOeiAQ,4475
7
+ kattis_cli/settings.py,sha256=d5q4dYj9VqDSqPalleh2oZWtND-1bPB0T2IwdajFrBg,591
8
+ kattis_cli/test_solution.py,sha256=g8LwVKx7Z1bSsXWs_JExCagBrrpKs7vL5PfaAoJHGKA,6114
9
+ kattis_cli/ui.py,sha256=dI06yncjm8sdLQkupPxGRODvQ6Gbawwuqn-67FrWg6I,2682
10
+ kattis_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ kattis_cli/utils/config.py,sha256=6SIuiXA9sFADo8RZUj8A6rXb3uqmrWrmmArfZrqNQdw,2293
12
+ kattis_cli/utils/languages.py,sha256=wlhl1Zng0gQAtsWW6FLxdkdzqeeY_qQ94wqa3NLypEU,8758
13
+ kattis_cli/utils/run_program.py,sha256=NWQ6vtTeWgkaW75r91FIHGXR5cAbeu8yMb5hwzpYFsg,2613
14
+ kattis_cli/utils/utility.py,sha256=XqiL7nVWCXs8DsoF6BqbkL0O9-348W-GKN1ScAzQdZ4,2732
15
+ kattis_cli-1.0.4.dist-info/LICENSE,sha256=JmBa4SEKBCDWEgiOZcISU4tUCpli6xSpVlSYgkBXSNQ,1067
16
+ kattis_cli-1.0.4.dist-info/METADATA,sha256=fs7fFZhce3ZgAMKeRSlMAYU2i_rVbyYN1LFDtAiUSek,6603
17
+ kattis_cli-1.0.4.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
18
+ kattis_cli-1.0.4.dist-info/entry_points.txt,sha256=kyzGN20VqUPR_H0J_jJUKT-10-cAMFLVegQ6C7tbHss,47
19
+ kattis_cli-1.0.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.8.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,18 +0,0 @@
1
- kattis_cli/.kattis-cli.toml,sha256=kGh0gmpFAnivyUC9hR8Y1ZqYJeTBWNyBQ9zOg4AInZ0,898
2
- kattis_cli/__init__.py,sha256=afN92pog2fGyicY6KNBofYbCBYbYj4Fpi_INUSpsc-E,402
3
- kattis_cli/download.py,sha256=aa0hBg_6Ou8SGrufgkNUjfWEHdmiu7U-W4d-yWHZNOA,5877
4
- kattis_cli/kattis.py,sha256=t5fog-tx1f1GeM-D3aa2YhuhxTnai_kV-x7miZbs6SI,13767
5
- kattis_cli/kattis_setup.py,sha256=rq_-Fz8oafxFJ8MOcxJqHQT8-bezgVHZXY2hm4EQF3U,4357
6
- kattis_cli/main.py,sha256=LDUUdG0eqiKL_zJX9V05OaqUIXKrOOwVAWOgN4TWWQQ,4121
7
- kattis_cli/settings.py,sha256=d5q4dYj9VqDSqPalleh2oZWtND-1bPB0T2IwdajFrBg,591
8
- kattis_cli/test_solution.py,sha256=KKYedt3ISQYLIoahLPgPizpApLQnOrBhmkew5aHW9BM,6627
9
- kattis_cli/ui.py,sha256=dI06yncjm8sdLQkupPxGRODvQ6Gbawwuqn-67FrWg6I,2682
10
- kattis_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- kattis_cli/utils/config.py,sha256=cr5ssEVWolDbqP8krts0YSK5vSLatKTveeHBRNsLRC8,2277
12
- kattis_cli/utils/run_program.py,sha256=NWQ6vtTeWgkaW75r91FIHGXR5cAbeu8yMb5hwzpYFsg,2613
13
- kattis_cli/utils/utility.py,sha256=JkypTfPOlDSxmZsh7KKcAVG2BC58t66corMBtcdvU7E,10532
14
- kattis_cli-1.0.2.dist-info/LICENSE,sha256=JmBa4SEKBCDWEgiOZcISU4tUCpli6xSpVlSYgkBXSNQ,1067
15
- kattis_cli-1.0.2.dist-info/METADATA,sha256=TImOazyOs7tJWBf_E-NP9x3-_InVey2bnqBxhjJMGOs,5961
16
- kattis_cli-1.0.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
17
- kattis_cli-1.0.2.dist-info/entry_points.txt,sha256=kyzGN20VqUPR_H0J_jJUKT-10-cAMFLVegQ6C7tbHss,47
18
- kattis_cli-1.0.2.dist-info/RECORD,,