sinol-make 1.6.0.dev4__tar.gz → 1.7.0__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.
Files changed (65) hide show
  1. {sinol_make-1.6.0.dev4/src/sinol_make.egg-info → sinol_make-1.7.0}/PKG-INFO +31 -2
  2. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/README.md +30 -1
  3. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/setup.cfg +1 -0
  4. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/__init__.py +12 -2
  5. sinol_make-1.7.0/src/sinol_make/commands/chkwer/__init__.py +137 -0
  6. sinol_make-1.7.0/src/sinol_make/commands/chkwer/chkwer_util.py +72 -0
  7. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/doc/__init__.py +3 -0
  8. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/export/__init__.py +4 -1
  9. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/gen/__init__.py +10 -4
  10. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/__init__.py +25 -9
  11. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/ingen_util.py +1 -1
  12. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/__init__.py +9 -17
  13. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/inwer_util.py +5 -1
  14. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/__init__.py +3 -0
  15. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/run/__init__.py +84 -341
  16. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/verify/__init__.py +13 -4
  17. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/contest_types/__init__.py +5 -0
  18. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/contest_types/default.py +24 -0
  19. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/contest_types/icpc.py +9 -0
  20. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/contest_types/oi.py +10 -1
  21. sinol_make-1.7.0/src/sinol_make/contest_types/oij.py +35 -0
  22. sinol_make-1.7.0/src/sinol_make/executors/__init__.py +65 -0
  23. sinol_make-1.7.0/src/sinol_make/executors/detailed.py +91 -0
  24. sinol_make-1.7.0/src/sinol_make/executors/oiejq.py +85 -0
  25. sinol_make-1.7.0/src/sinol_make/executors/time.py +113 -0
  26. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/cache.py +3 -3
  27. sinol_make-1.7.0/src/sinol_make/helpers/classinit.py +90 -0
  28. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/compile.py +12 -7
  29. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/package_util.py +22 -4
  30. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/parsers.py +6 -0
  31. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/paths.py +8 -0
  32. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/interfaces/BaseCommand.py +7 -0
  33. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/interfaces/Errors.py +1 -1
  34. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/oiejq/__init__.py +20 -8
  35. sinol_make-1.7.0/src/sinol_make/structs/chkwer_structs.py +68 -0
  36. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/status_structs.py +27 -6
  37. sinol_make-1.7.0/src/sinol_make/task_type/__init__.py +179 -0
  38. sinol_make-1.7.0/src/sinol_make/task_type/interactive.py +238 -0
  39. sinol_make-1.7.0/src/sinol_make/task_type/normal.py +37 -0
  40. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/util.py +2 -0
  41. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0/src/sinol_make.egg-info}/PKG-INFO +31 -2
  42. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make.egg-info/SOURCES.txt +12 -0
  43. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make.egg-info/entry_points.txt +1 -0
  44. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/tests/test_oiejq.py +41 -4
  45. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/LICENSE +0 -0
  46. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/pyproject.toml +0 -0
  47. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/init/__init__.py +0 -0
  48. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/outgen_util.py +0 -0
  49. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/__init__.py +0 -0
  50. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/compiler.py +0 -0
  51. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/func_cache.py +0 -0
  52. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/helpers/printer.py +0 -0
  53. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/interfaces/__init__.py +0 -0
  54. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/oiejq/perf_test.py +0 -0
  55. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/__init__.py +0 -0
  56. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/cache_structs.py +0 -0
  57. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/compiler_structs.py +0 -0
  58. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/gen_structs.py +0 -0
  59. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/inwer_structs.py +0 -0
  60. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make/structs/run_structs.py +0 -0
  61. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make.egg-info/dependency_links.txt +0 -0
  62. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make.egg-info/requires.txt +0 -0
  63. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/src/sinol_make.egg-info/top_level.txt +0 -0
  64. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/tests/test_multiple_arguments.py +0 -0
  65. {sinol_make-1.6.0.dev4 → sinol_make-1.7.0}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sinol_make
3
- Version: 1.6.0.dev4
3
+ Version: 1.7.0
4
4
  Summary: CLI tool for creating sio2 task packages
5
5
  Author: Mateusz Masiarz
6
6
  Author-email: m.masiarz@fri.edu.pl
@@ -45,6 +45,7 @@ with features such as:
45
45
  - [Why?](#why)
46
46
  - [Installation](#installation)
47
47
  - [Usage](#usage)
48
+ - [Contest types](#contest-types)
48
49
  - [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code)
49
50
 
50
51
  ### Why?
@@ -93,7 +94,7 @@ activate-global-python-argcomplete
93
94
 
94
95
  ### Usage
95
96
 
96
- The availabe commands (see `sinol-make --help`) are:
97
+ The available commands (see `sinol-make --help`) are:
97
98
 
98
99
  - `sinol-make run` -- Runs selected solutions (by default all solutions) on selected tests (by default all tests) with a given number
99
100
  of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
@@ -113,6 +114,9 @@ Run `sinol-make ingen --help` to see available flags.
113
114
  program to use, what tests to check and how many CPUs to use. Run `sinol-make inwer --help` to see available flags.
114
115
  - `sinol-make export` -- Creates archive ready to upload to sio2 or szkopul. Run `sinol-make export --help` to see all available flags.
115
116
  - `sinol-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `sinol-make doc --help` to see all available flags.
117
+ - `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
118
+ generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
119
+ address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
116
120
  - `sinol-make init [id]` -- Creates package from template [on github](https://github.com/sio2project/sinol-make/tree/main/example_package) and sets task id to provided `[id]`. Requires an internet connection to run.
117
121
 
118
122
  You can also run multiple commands at once, for example:
@@ -121,6 +125,31 @@ You can also run multiple commands at once, for example:
121
125
  sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
122
126
  ```
123
127
 
128
+ There are also available short aliases for the commands:
129
+ - `sinol-make r` for `sinol-make run`
130
+ - `sinol-make g` for `sinol-make gen`
131
+ - `sinol-make i` for `sinol-make inwer`
132
+ - `sinol-make e` for `sinol-make export`
133
+ - `sinol-make d` for `sinol-make doc`
134
+ - `sinol-make v` for `sinol-make verify`
135
+ - `sm` for `sinol-make`
136
+
137
+ ### Contest types
138
+
139
+ `sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
140
+ the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
141
+ features:
142
+
143
+ | Feature | `default` | `oi` | `oij` | `icpc` |
144
+ |-----------------------------------------------------------------------------------------------------|-----------|-------|-------|--------|
145
+ | Max score | 100 | 100 | 100 | 1 |
146
+ | Default time tool | oiejq | oiejq | oiejq | time |
147
+ | Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
148
+ | Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
149
+ | Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
150
+ | Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
151
+ | Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
152
+
124
153
  ### Reporting bugs and contributing code
125
154
 
126
155
  - Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
@@ -15,6 +15,7 @@ with features such as:
15
15
  - [Why?](#why)
16
16
  - [Installation](#installation)
17
17
  - [Usage](#usage)
18
+ - [Contest types](#contest-types)
18
19
  - [Reporting bugs and contributing code](#reporting-bugs-and-contributing-code)
19
20
 
20
21
  ### Why?
@@ -63,7 +64,7 @@ activate-global-python-argcomplete
63
64
 
64
65
  ### Usage
65
66
 
66
- The availabe commands (see `sinol-make --help`) are:
67
+ The available commands (see `sinol-make --help`) are:
67
68
 
68
69
  - `sinol-make run` -- Runs selected solutions (by default all solutions) on selected tests (by default all tests) with a given number
69
70
  of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
@@ -83,6 +84,9 @@ Run `sinol-make ingen --help` to see available flags.
83
84
  program to use, what tests to check and how many CPUs to use. Run `sinol-make inwer --help` to see available flags.
84
85
  - `sinol-make export` -- Creates archive ready to upload to sio2 or szkopul. Run `sinol-make export --help` to see all available flags.
85
86
  - `sinol-make doc` -- Compiles all LaTeX files in doc/ directory to PDF. Run `sinol-make doc --help` to see all available flags.
87
+ - `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
88
+ generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
89
+ address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
86
90
  - `sinol-make init [id]` -- Creates package from template [on github](https://github.com/sio2project/sinol-make/tree/main/example_package) and sets task id to provided `[id]`. Requires an internet connection to run.
87
91
 
88
92
  You can also run multiple commands at once, for example:
@@ -91,6 +95,31 @@ You can also run multiple commands at once, for example:
91
95
  sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
92
96
  ```
93
97
 
98
+ There are also available short aliases for the commands:
99
+ - `sinol-make r` for `sinol-make run`
100
+ - `sinol-make g` for `sinol-make gen`
101
+ - `sinol-make i` for `sinol-make inwer`
102
+ - `sinol-make e` for `sinol-make export`
103
+ - `sinol-make d` for `sinol-make doc`
104
+ - `sinol-make v` for `sinol-make verify`
105
+ - `sm` for `sinol-make`
106
+
107
+ ### Contest types
108
+
109
+ `sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
110
+ the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
111
+ features:
112
+
113
+ | Feature | `default` | `oi` | `oij` | `icpc` |
114
+ |-----------------------------------------------------------------------------------------------------|-----------|-------|-------|--------|
115
+ | Max score | 100 | 100 | 100 | 1 |
116
+ | Default time tool | oiejq | oiejq | oiejq | time |
117
+ | Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
118
+ | Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
119
+ | Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
120
+ | Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
121
+ | Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
122
+
94
123
  ### Reporting bugs and contributing code
95
124
 
96
125
  - Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
@@ -44,6 +44,7 @@ tests =
44
44
  [options.entry_points]
45
45
  console_scripts =
46
46
  sinol-make = sinol_make:main
47
+ sm = sinol_make:main
47
48
 
48
49
  [tool:pytest]
49
50
  testpaths =
@@ -6,7 +6,12 @@ import argcomplete
6
6
 
7
7
  from sinol_make import util, oiejq
8
8
 
9
- __version__ = "1.6.0.dev4"
9
+ # Required for side effects
10
+ from sinol_make.task_type.normal import NormalTaskType # noqa
11
+ from sinol_make.task_type.interactive import InteractiveTaskType # noqa
12
+
13
+
14
+ __version__ = "1.7.0"
10
15
 
11
16
 
12
17
  def configure_parsers():
@@ -33,7 +38,7 @@ def configure_parsers():
33
38
 
34
39
  def check_oiejq():
35
40
  if util.is_linux() and not oiejq.check_oiejq():
36
- print(util.warning('`oiejq` in `~/.local/bin/` not found, installing now...'))
41
+ print(util.warning('Up to date `oiejq` in `~/.local/bin/` not found, installing new version...'))
37
42
  try:
38
43
  if oiejq.install_oiejq():
39
44
  print(util.info('`oiejq` was successfully installed.'))
@@ -52,11 +57,16 @@ def main_exn():
52
57
  curr_args = []
53
58
  commands = util.get_commands()
54
59
  commands_dict = {command.get_name(): command for command in commands}
60
+ commands_short_dict = {command.get_short_name(): command for command in commands if command.get_short_name()}
55
61
  for arg in sys.argv[1:]:
56
62
  if arg in commands_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
57
63
  if curr_args:
58
64
  arguments.append(curr_args)
59
65
  curr_args = [arg]
66
+ elif arg in commands_short_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
67
+ if curr_args:
68
+ arguments.append(curr_args)
69
+ curr_args = [commands_short_dict[arg].get_name()]
60
70
  else:
61
71
  curr_args.append(arg)
62
72
  if curr_args:
@@ -0,0 +1,137 @@
1
+ import os
2
+ import subprocess
3
+ import threading
4
+ from typing import Dict, List
5
+ import multiprocessing as mp
6
+
7
+ from sinol_make import util, contest_types
8
+ from sinol_make.commands.chkwer import chkwer_util
9
+ from sinol_make.commands.outgen import outgen_util
10
+ from sinol_make.helpers import package_util, parsers, compiler, compile, printer, paths
11
+ from sinol_make.interfaces.BaseCommand import BaseCommand
12
+ from sinol_make.structs.chkwer_structs import TestResult, ChkwerExecution, TableData, RunResult
13
+
14
+
15
+ class Command(BaseCommand):
16
+ """
17
+ Class for `chkwer` command.
18
+ """
19
+
20
+ def get_name(self):
21
+ return "chkwer"
22
+
23
+ def get_short_name(self):
24
+ return "c"
25
+
26
+ def configure_subparser(self, subparser):
27
+ parser = subparser.add_parser(
28
+ self.get_name(),
29
+ help='Run checker with model solution and print results',
30
+ description='Run checker with model solution and print results. '
31
+ 'Prints a table with points and checker\'s comments. '
32
+ 'This command fails if the model solution didn\'t '
33
+ 'score maximum points.'
34
+ )
35
+ parser.add_argument('-t', '--tests', type=str, nargs='+',
36
+ help='test to run, for example in/abc{0,1}*')
37
+ parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests')
38
+ parsers.add_compilation_arguments(parser)
39
+ return parser
40
+
41
+ def compile(self, file_path, exe_path, args, name, compilation_flags):
42
+ print(f'Compiling {name}... ', end='')
43
+ compilers = compiler.verify_compilers(args, [file_path])
44
+ exe, compile_log_path = compile.compile_file(file_path, exe_path, compilers, compilation_flags,
45
+ use_fsanitize=False, use_extras=False)
46
+ if exe is None:
47
+ print(util.error('ERROR'))
48
+ compile.print_compile_log(compile_log_path)
49
+ util.exit_with_error(f'Failed {name} compilation.')
50
+ else:
51
+ print(util.info('OK'))
52
+ return exe
53
+
54
+ def run_test(self, execution: ChkwerExecution) -> RunResult:
55
+ """
56
+ Verifies a test and returns the result of chkwer on this test.
57
+ """
58
+ output_file = paths.get_chkwer_path(os.path.basename(execution.out_test_path))
59
+ with open(execution.in_test_path, 'r') as inf, open(output_file, 'w') as outf:
60
+ process = subprocess.Popen([execution.model_exe], stdin=inf, stdout=outf)
61
+ process.wait()
62
+ ok, points, comment = self.task_type.check_output(execution.in_test_path, output_file, execution.out_test_path)
63
+
64
+ return RunResult(execution.in_test_path, ok, int(points), comment)
65
+
66
+ def run_and_print_table(self) -> Dict[str, TestResult]:
67
+ os.makedirs(paths.get_chkwer_path(), exist_ok=True)
68
+ results = {}
69
+ sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id))
70
+ executions: List[ChkwerExecution] = []
71
+ for test in sorted_tests:
72
+ results[test] = TestResult(test, self.task_id)
73
+ executions.append(ChkwerExecution(test, results[test].test_name, package_util.get_out_from_in(test),
74
+ self.checker_executable, self.model_executable))
75
+
76
+ has_terminal, terminal_width, terminal_height = util.get_terminal_size()
77
+ table_data = TableData(results, 0, self.task_id, self.contest_type.max_score_per_test())
78
+ if has_terminal:
79
+ run_event = threading.Event()
80
+ run_event.set()
81
+ thr = threading.Thread(target=printer.printer_thread, args=(run_event, chkwer_util.print_view, table_data))
82
+ thr.start()
83
+
84
+ keyboard_interrupt = False
85
+ try:
86
+ with mp.Pool(self.cpus) as pool:
87
+ for i, result in enumerate(pool.imap(self.run_test, executions)):
88
+ table_data.results[result.test_path].set_results(result.points, result.ok, result.comment)
89
+ table_data.i = i
90
+ except KeyboardInterrupt:
91
+ keyboard_interrupt = True
92
+
93
+ if has_terminal:
94
+ run_event.clear()
95
+ thr.join()
96
+
97
+ print("\n".join(chkwer_util.print_view(terminal_width, terminal_height, table_data)[0]))
98
+ if keyboard_interrupt:
99
+ util.exit_with_error("Keyboard interrupt.")
100
+ return results
101
+
102
+ def run(self, args):
103
+ args = util.init_package_command(args)
104
+ self.task_id = package_util.get_task_id()
105
+ self.task_type = package_util.get_task_type("time", None)
106
+ self.contest_type = contest_types.get_contest_type()
107
+
108
+ if self.task_type.name() != "normal":
109
+ util.exit_with_error("chkwer can be run only for normal tasks.")
110
+
111
+ self.cpus = args.cpus or util.default_cpu_count()
112
+ self.tests = package_util.get_tests(self.task_id, args.tests)
113
+
114
+ if len(self.tests) == 0:
115
+ util.exit_with_error("No tests found.")
116
+ else:
117
+ print('Will run on tests: ' + util.bold(', '.join(self.tests)))
118
+ util.change_stack_size_to_unlimited()
119
+
120
+ additional_files = self.task_type.additional_files_to_compile()
121
+ if len(additional_files) == 0:
122
+ util.exit_with_error("Checker not found.")
123
+ if len(additional_files) != 1:
124
+ util.exit_with_error("More than one file to compile found. How is that possible?")
125
+ checker_info = additional_files[0]
126
+ model_solution = outgen_util.get_correct_solution(self.task_id)
127
+ self.checker_executable = self.compile(checker_info[0], checker_info[1], args, "checker",
128
+ args.compile_mode)
129
+ self.model_executable = self.compile(model_solution, package_util.get_executable(model_solution), args,
130
+ "model solution", args.compile_mode)
131
+ print()
132
+
133
+ results = self.run_and_print_table()
134
+ for result in results.values():
135
+ if not result.ok or result.points != self.contest_type.max_score_per_test():
136
+ util.exit_with_error("Model solution didn't score maximum points.")
137
+ print(util.info("Checker verification successful."))
@@ -0,0 +1,72 @@
1
+ import sys
2
+ from io import StringIO
3
+
4
+ from sinol_make import util
5
+ from sinol_make.commands.inwer.inwer_util import sort_tests
6
+ from sinol_make.structs.chkwer_structs import TableData
7
+
8
+
9
+ def print_view(term_width, term_height, table_data: TableData):
10
+ """
11
+ Prints current results of test verification.
12
+ """
13
+
14
+ previous_stdout = sys.stdout
15
+ new_stdout = StringIO()
16
+ sys.stdout = new_stdout
17
+
18
+ results = table_data.results
19
+ column_lengths = [0, len('Points') + 1, 0]
20
+ tests = []
21
+ for result in results.values():
22
+ column_lengths[0] = max(column_lengths[0], len(result.test_name))
23
+ tests.append(result.test_path)
24
+ tests = sort_tests(tests, table_data.task_id)
25
+
26
+ # 6 is for " | " between columns, 3 for margin.
27
+ column_lengths[2] = max(10, term_width - column_lengths[0] - column_lengths[1] - 6 - 3)
28
+ margin = " "
29
+
30
+ def print_line_separator():
31
+ res = "-" * (column_lengths[0] + 3) + "+" + "-" * (column_lengths[1] + 1) + "+"
32
+ res += "-" * (term_width - len(res) - 1)
33
+ print(res)
34
+
35
+ print_line_separator()
36
+
37
+ print(margin + "Test".ljust(column_lengths[0]) + " | " + "Points" + " | " + "Comment")
38
+ print_line_separator()
39
+
40
+ last_group = None
41
+ for test_path in tests:
42
+ result = results[test_path]
43
+ if last_group is not None and last_group != result.test_group:
44
+ print_line_separator()
45
+ last_group = result.test_group
46
+ print(margin + result.test_name.ljust(column_lengths[0]) + " | ", end='')
47
+
48
+ if result.run:
49
+ if result.ok:
50
+ if result.points == table_data.max_score:
51
+ print(util.info(str(result.points).ljust(column_lengths[1] - 1)), end='')
52
+ else:
53
+ print(util.warning(str(result.points).ljust(column_lengths[1] - 1)), end='')
54
+ else:
55
+ print(util.error(str(result.points).ljust(column_lengths[1] - 1)), end='')
56
+ else:
57
+ print(util.warning("...".ljust(column_lengths[1] - 1)), end='')
58
+ print(" | ", end='')
59
+
60
+ output = []
61
+ if result.run:
62
+ if result.comment:
63
+ print(result.comment)
64
+ else:
65
+ print(util.color_gray("No comment"))
66
+
67
+ print_line_separator()
68
+ print()
69
+ print()
70
+
71
+ sys.stdout = previous_stdout
72
+ return new_stdout.getvalue().splitlines(), None, "Use arrows to move."
@@ -17,6 +17,9 @@ class Command(BaseCommand):
17
17
  def get_name(self):
18
18
  return "doc"
19
19
 
20
+ def get_short_name(self):
21
+ return "d"
22
+
20
23
  def compile_file_latex_div(self, file_path):
21
24
  print(f'Compiling {os.path.basename(file_path)} (latex to dvi)...')
22
25
  os.chdir(os.path.dirname(file_path))
@@ -23,6 +23,9 @@ class Command(BaseCommand):
23
23
  def get_name(self):
24
24
  return "export"
25
25
 
26
+ def get_short_name(self):
27
+ return "e"
28
+
26
29
  def configure_subparser(self, subparser: argparse.ArgumentParser):
27
30
  parser = subparser.add_parser(
28
31
  self.get_name(),
@@ -119,7 +122,7 @@ class Command(BaseCommand):
119
122
  if num_tests == 0:
120
123
  print(util.warning('No ocen tests found.'))
121
124
  elif os.path.exists(dlazaw_dir):
122
- print(util.warning('Skipping ocen arhive creation because dlazaw directory exists.'))
125
+ print(util.warning('Skipping ocen archive creation because dlazaw directory exists.'))
123
126
  else:
124
127
  shutil.make_archive(os.path.join(attachments_dir, f'{self.task_id}ocen'), 'zip', tmpdir)
125
128
 
@@ -3,7 +3,7 @@ import argparse
3
3
  from sinol_make import util
4
4
  from sinol_make.commands.ingen import Command as IngenCommand
5
5
  from sinol_make.commands.outgen import Command as OutgenCommand
6
- from sinol_make.helpers import parsers
6
+ from sinol_make.helpers import parsers, package_util
7
7
  from sinol_make.interfaces.BaseCommand import BaseCommand
8
8
 
9
9
 
@@ -15,6 +15,9 @@ class Command(BaseCommand):
15
15
  def get_name(self):
16
16
  return "gen"
17
17
 
18
+ def get_short_name(self):
19
+ return "g"
20
+
18
21
  def configure_subparser(self, subparser):
19
22
  parser = subparser.add_parser(
20
23
  self.get_name(),
@@ -34,9 +37,7 @@ class Command(BaseCommand):
34
37
  parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
35
38
  parser.add_argument('-n', '--no-validate', default=False, action='store_true',
36
39
  help='do not validate test contents')
37
- parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
38
- help='Use -fsanitize=address,undefined for ingen compilation. Warning: this may fail on some '
39
- 'systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
40
+ parsers.add_fsanitize_argument(parser)
40
41
  parsers.add_compilation_arguments(parser)
41
42
  return parser
42
43
 
@@ -46,6 +47,7 @@ class Command(BaseCommand):
46
47
  self.args = args
47
48
  self.ins = args.only_inputs
48
49
  self.outs = args.only_outputs
50
+ self.task_type = package_util.get_task_type_cls()
49
51
  # If no arguments are specified, generate both input and output files.
50
52
  if not self.ins and not self.outs:
51
53
  self.ins = True
@@ -55,6 +57,10 @@ class Command(BaseCommand):
55
57
  command = IngenCommand()
56
58
  command.run(args)
57
59
 
60
+ if not self.task_type.run_outgen():
61
+ print(util.warning("Outgen is not supported for this task type."))
62
+ return
63
+
58
64
  if self.outs:
59
65
  command = OutgenCommand()
60
66
  command.run(args)
@@ -31,19 +31,39 @@ class Command(BaseCommand):
31
31
  parser.add_argument('-n', '--no-validate', default=False, action='store_true',
32
32
  help='do not validate test contents')
33
33
  parsers.add_cpus_argument(parser, 'number of cpus used for validating tests')
34
- parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
35
- help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
36
- 'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
34
+ parsers.add_fsanitize_argument(parser)
37
35
  parsers.add_compilation_arguments(parser)
38
36
  return parser
39
37
 
38
+ def delete_dangling_files(self, dates):
39
+ to_delete = set()
40
+ for test in glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")):
41
+ basename = os.path.basename(test)
42
+ if basename in dates and dates[basename] == os.path.getmtime(test):
43
+ to_delete.add(os.path.basename(test))
44
+ if to_delete:
45
+ config = package_util.get_config()
46
+ if 'sinol_static_tests' not in config:
47
+ print(util.warning('Old input files won\'t be deleted, '
48
+ 'because static tests are not defined. '
49
+ 'You can define them in config.yml with `sinol_static_tests` key.'))
50
+ else:
51
+ static_files = config['sinol_static_tests']
52
+ if isinstance(static_files, str):
53
+ static_files = [static_files]
54
+ static_files = set([os.path.basename(test) for test in static_files])
55
+ to_delete = to_delete - static_files
56
+ if to_delete:
57
+ print('Cleaning up old input files.')
58
+ for test in to_delete:
59
+ os.remove(os.path.join(os.getcwd(), "in", test))
60
+
40
61
  def run(self, args: argparse.Namespace):
41
62
  args = util.init_package_command(args)
42
63
 
43
64
  self.args = args
44
65
 
45
66
  self.task_id = package_util.get_task_id()
46
- package_util.validate_test_names(self.task_id)
47
67
  util.change_stack_size_to_unlimited()
48
68
  self.ingen = get_ingen(self.task_id, args.ingen_path)
49
69
  print(f'Using ingen file {os.path.basename(self.ingen)}')
@@ -65,11 +85,7 @@ class Command(BaseCommand):
65
85
  else:
66
86
  util.exit_with_error('Failed to generate input files.')
67
87
 
68
- print('Cleaning up old input files.')
69
- for test in glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")):
70
- basename = os.path.basename(test)
71
- if basename in dates and dates[basename] == os.path.getmtime(test):
72
- os.unlink(test)
88
+ self.delete_dangling_files(dates)
73
89
 
74
90
  with open(paths.get_cache_path("input_tests"), "w") as f:
75
91
  f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))))
@@ -58,7 +58,7 @@ def compile_ingen(ingen_path: str, args: argparse.Namespace, compilation_flags='
58
58
  compilers = compiler.verify_compilers(args, [ingen_path])
59
59
  ingen_exe, compile_log_path = compile.compile_file(ingen_path, package_util.get_executable(ingen_path),
60
60
  compilers, compilation_flags, use_fsanitize=use_fsanitize,
61
- additional_flags='-D_INGEN')
61
+ additional_flags='-D_INGEN', use_extras=False)
62
62
 
63
63
  if ingen_exe is None:
64
64
  compile.print_compile_log(compile_log_path)
@@ -8,7 +8,7 @@ import multiprocessing as mp
8
8
  from functools import cmp_to_key
9
9
  from typing import Dict, List
10
10
 
11
- from sinol_make import util
11
+ from sinol_make import util, contest_types
12
12
  from sinol_make.structs.inwer_structs import TestResult, InwerExecution, VerificationResult, TableData
13
13
  from sinol_make.helpers import package_util, printer, paths, parsers
14
14
  from sinol_make.interfaces.BaseCommand import BaseCommand
@@ -23,6 +23,9 @@ class Command(BaseCommand):
23
23
  def get_name(self):
24
24
  return "inwer"
25
25
 
26
+ def get_short_name(self):
27
+ return "i"
28
+
26
29
  def configure_subparser(self, subparser: argparse.ArgumentParser):
27
30
  parser = subparser.add_parser(
28
31
  self.get_name(),
@@ -37,9 +40,7 @@ class Command(BaseCommand):
37
40
  parser.add_argument('-t', '--tests', type=str, nargs='+',
38
41
  help='test to verify, for example in/abc{0,1}*')
39
42
  parsers.add_cpus_argument(parser, 'number of cpus to use when verifying tests')
40
- parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
41
- help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
42
- 'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
43
+ parsers.add_fsanitize_argument(parser)
43
44
  parsers.add_compilation_arguments(parser)
44
45
  return parser
45
46
 
@@ -53,17 +54,7 @@ class Command(BaseCommand):
53
54
 
54
55
  command = [execution.inwer_exe_path, os.path.basename(execution.test_path)]
55
56
  with open(execution.test_path, 'r') as test:
56
- process = subprocess.Popen(command, stdin=test, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
57
- preexec_fn=os.setsid)
58
-
59
- def sigint_handler(signum, frame):
60
- try:
61
- os.killpg(os.getpgid(process.pid), signal.SIGTERM)
62
- except ProcessLookupError:
63
- pass
64
- sys.exit(1)
65
- signal.signal(signal.SIGINT, sigint_handler)
66
-
57
+ process = subprocess.Popen(command, stdin=test, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
67
58
  process.wait()
68
59
  exit_code = process.returncode
69
60
  out, _ = process.communicate()
@@ -205,6 +196,7 @@ class Command(BaseCommand):
205
196
 
206
197
  self.cpus = args.cpus or util.default_cpu_count()
207
198
  self.tests = package_util.get_tests(self.task_id, args.tests)
199
+ self.contest_type = contest_types.get_contest_type()
208
200
 
209
201
  if len(self.tests) == 0:
210
202
  util.exit_with_error('No tests found.')
@@ -223,7 +215,7 @@ class Command(BaseCommand):
223
215
 
224
216
  if len(failed_tests) > 0:
225
217
  util.exit_with_error(f'Verification failed for tests: {", ".join(failed_tests)}')
226
- else:
218
+ elif self.contest_type.verify_tests_order():
227
219
  print("Verifying tests order...")
228
220
  self.verify_tests_order()
229
- print(util.info('Verification successful.'))
221
+ print(util.info('Verification successful.'))
@@ -36,7 +36,7 @@ def compile_inwer(inwer_path: str, args: argparse.Namespace, compilation_flags='
36
36
  compilers = compiler.verify_compilers(args, [inwer_path])
37
37
  inwer_exe, compile_log_path = compile.compile_file(inwer_path, package_util.get_executable(inwer_path), compilers,
38
38
  compilation_flags, use_fsanitize=use_fsanitize,
39
- additional_flags='-D_INWER')
39
+ additional_flags='-D_INWER', use_extras=False)
40
40
 
41
41
  if inwer_exe is None:
42
42
  compile.print_compile_log(compile_log_path)
@@ -86,8 +86,12 @@ def print_view(term_width, term_height, table_data: TableData):
86
86
  " | " + "Output")
87
87
  print_line_separator()
88
88
 
89
+ last_group = None
89
90
  for test_path in tests:
90
91
  result = results[test_path]
92
+ if last_group is not None and last_group != result.test_group:
93
+ print_line_separator()
94
+ last_group = result.test_group
91
95
  print(margin + result.test_name.ljust(column_lengths[0]) + " | ", end='')
92
96
  print(result.test_group.ljust(column_lengths[1] - 1) + " | ", end='')
93
97
 
@@ -109,6 +109,9 @@ class Command(BaseCommand):
109
109
 
110
110
  self.args = args
111
111
  self.task_id = package_util.get_task_id()
112
+ self.task_type = package_util.get_task_type_cls()
113
+ if not self.task_type.run_outgen():
114
+ util.exit_with_error('Output generation is not supported for this task type.')
112
115
  package_util.validate_test_names(self.task_id)
113
116
  util.change_stack_size_to_unlimited()
114
117
  cache.check_correct_solution(self.task_id)