sinol-make 1.6.0.dev3__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.dev3/src/sinol_make.egg-info → sinol_make-1.7.0}/PKG-INFO +31 -2
  2. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/README.md +30 -1
  3. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/setup.cfg +1 -0
  4. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/__init__.py +24 -12
  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.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/doc/__init__.py +6 -3
  8. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/export/__init__.py +7 -5
  9. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/gen/__init__.py +11 -8
  10. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/__init__.py +28 -14
  11. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/ingen/ingen_util.py +1 -1
  12. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/__init__.py +12 -22
  13. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/inwer/inwer_util.py +5 -1
  14. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/__init__.py +6 -10
  15. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/run/__init__.py +103 -351
  16. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/verify/__init__.py +17 -4
  17. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/__init__.py +5 -0
  18. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/default.py +24 -0
  19. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/contest_types/icpc.py +9 -0
  20. {sinol_make-1.6.0.dev3 → 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.dev3 → 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.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/compile.py +12 -7
  29. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/compiler.py +11 -6
  30. sinol_make-1.7.0/src/sinol_make/helpers/func_cache.py +30 -0
  31. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/package_util.py +54 -11
  32. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/parsers.py +17 -3
  33. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/paths.py +8 -0
  34. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/BaseCommand.py +7 -0
  35. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/Errors.py +1 -1
  36. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/oiejq/__init__.py +20 -8
  37. sinol_make-1.7.0/src/sinol_make/structs/chkwer_structs.py +68 -0
  38. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/status_structs.py +27 -6
  39. sinol_make-1.7.0/src/sinol_make/task_type/__init__.py +179 -0
  40. sinol_make-1.7.0/src/sinol_make/task_type/interactive.py +238 -0
  41. sinol_make-1.7.0/src/sinol_make/task_type/normal.py +37 -0
  42. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/util.py +4 -8
  43. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0/src/sinol_make.egg-info}/PKG-INFO +31 -2
  44. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/SOURCES.txt +13 -0
  45. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/entry_points.txt +1 -0
  46. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_oiejq.py +41 -4
  47. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_util.py +8 -5
  48. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/LICENSE +0 -0
  49. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/pyproject.toml +0 -0
  50. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/init/__init__.py +0 -0
  51. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/commands/outgen/outgen_util.py +0 -0
  52. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/__init__.py +0 -0
  53. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/helpers/printer.py +0 -0
  54. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/interfaces/__init__.py +0 -0
  55. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/oiejq/perf_test.py +0 -0
  56. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/__init__.py +0 -0
  57. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/cache_structs.py +0 -0
  58. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/compiler_structs.py +0 -0
  59. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/gen_structs.py +0 -0
  60. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/inwer_structs.py +0 -0
  61. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make/structs/run_structs.py +0 -0
  62. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/dependency_links.txt +0 -0
  63. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/requires.txt +0 -0
  64. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/src/sinol_make.egg-info/top_level.txt +0 -0
  65. {sinol_make-1.6.0.dev3 → sinol_make-1.7.0}/tests/test_multiple_arguments.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sinol_make
3
- Version: 1.6.0.dev3
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.dev3"
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.'))
@@ -50,28 +55,35 @@ def main_exn():
50
55
  parser = configure_parsers()
51
56
  arguments = []
52
57
  curr_args = []
58
+ commands = util.get_commands()
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()}
53
61
  for arg in sys.argv[1:]:
54
- if arg in util.get_command_names() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
62
+ if arg in commands_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
55
63
  if curr_args:
56
64
  arguments.append(curr_args)
57
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()]
58
70
  else:
59
71
  curr_args.append(arg)
60
72
  if curr_args:
61
73
  arguments.append(curr_args)
62
- commands = util.get_commands()
74
+ if not arguments:
75
+ parser.print_help()
76
+ exit(1)
63
77
  check_oiejq()
64
78
 
65
79
  for curr_args in arguments:
66
80
  args = parser.parse_args(curr_args)
67
- command_found = False
68
- for command in commands:
69
- if command.get_name() == args.command:
70
- command_found = True
71
- if len(arguments) > 1:
72
- print(f' {command.get_name()} command '.center(util.get_terminal_size()[1], '='))
73
- command.run(args)
74
- if not command_found:
81
+ command = commands_dict.get(args.command, None)
82
+ if command:
83
+ if len(arguments) > 1:
84
+ print(f' {command.get_name()} command '.center(util.get_terminal_size()[1], '='))
85
+ command.run(args)
86
+ else:
75
87
  parser.print_help()
76
88
  exit(1)
77
89
 
@@ -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,8 +17,11 @@ 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
- print(util.info(f'Compiling {os.path.basename(file_path)} (latex to dvi)...'))
24
+ print(f'Compiling {os.path.basename(file_path)} (latex to dvi)...')
22
25
  os.chdir(os.path.dirname(file_path))
23
26
  subprocess.run(['latex', file_path])
24
27
  dvi_file = os.path.splitext(file_path)[0] + '.dvi'
@@ -35,7 +38,7 @@ class Command(BaseCommand):
35
38
  return True
36
39
 
37
40
  def compile_pdf_latex(self, file_path):
38
- print(util.info(f'Compiling {os.path.basename(file_path)} (pdflatex)...'))
41
+ print(f'Compiling {os.path.basename(file_path)} (pdflatex)...')
39
42
  os.chdir(os.path.dirname(file_path))
40
43
  subprocess.run(['pdflatex', file_path])
41
44
  pdf_file = os.path.splitext(file_path)[0] + '.pdf'
@@ -62,7 +65,7 @@ class Command(BaseCommand):
62
65
  for pattern in self.LOG_PATTERNS:
63
66
  for file in glob.glob(os.path.join(os.getcwd(), 'doc', pattern)):
64
67
  os.rename(file, os.path.join(output_dir, os.path.basename(file)))
65
- print(util.info(f'Compilation log files can be found in {os.path.relpath(output_dir, os.getcwd())}'))
68
+ print(f'Compilation log files can be found in {os.path.relpath(output_dir, os.getcwd())}')
66
69
 
67
70
  def configure_subparser(self, subparser: argparse.ArgumentParser):
68
71
  parser = subparser.add_parser(
@@ -23,15 +23,15 @@ 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(),
29
32
  help='Create archive for oioioi upload',
30
33
  description='Creates archive in the current directory ready to upload to sio2 or szkopul.')
31
- parser.add_argument('-c', '--cpus', type=int,
32
- help=f'number of cpus to use to generate output files '
33
- f'(default: {util.default_cpu_count()})',
34
- default=util.default_cpu_count())
34
+ parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
35
35
  parser.add_argument('--no-statement', dest='no_statement', action='store_true',
36
36
  help='allow export without statement')
37
37
  parser.add_argument('--export-ocen', dest='export_ocen', action='store_true',
@@ -118,12 +118,14 @@ class Command(BaseCommand):
118
118
  shutil.copy(test, os.path.join(ocen_dir, ext, os.path.basename(test)))
119
119
  num_tests += 1
120
120
 
121
+ dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
121
122
  if num_tests == 0:
122
123
  print(util.warning('No ocen tests found.'))
124
+ elif os.path.exists(dlazaw_dir):
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
 
126
- dlazaw_dir = os.path.join(os.getcwd(), 'dlazaw')
127
129
  if os.path.exists(dlazaw_dir):
128
130
  print('Archiving dlazaw directory and adding to attachments.')
129
131
  os.makedirs(os.path.join(tmpdir, 'dlazaw'), exist_ok=True)
@@ -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(),
@@ -31,15 +34,10 @@ class Command(BaseCommand):
31
34
  help='path to ingen source file, for example prog/abcingen.cpp')
32
35
  parser.add_argument('-i', '--only-inputs', action='store_true', help='generate input files only')
33
36
  parser.add_argument('-o', '--only-outputs', action='store_true', help='generate output files only')
34
- parser.add_argument('-c', '--cpus', type=int,
35
- help=f'number of cpus to use to generate output files '
36
- f'(default: {util.default_cpu_count()})',
37
- default=util.default_cpu_count())
37
+ parsers.add_cpus_argument(parser, 'number of cpus to use to generate output files')
38
38
  parser.add_argument('-n', '--no-validate', default=False, action='store_true',
39
39
  help='do not validate test contents')
40
- parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
41
- help='Use -fsanitize=address,undefined for ingen compilation. Warning: this may fail on some '
42
- 'systems. To fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
40
+ parsers.add_fsanitize_argument(parser)
43
41
  parsers.add_compilation_arguments(parser)
44
42
  return parser
45
43
 
@@ -49,6 +47,7 @@ class Command(BaseCommand):
49
47
  self.args = args
50
48
  self.ins = args.only_inputs
51
49
  self.outs = args.only_outputs
50
+ self.task_type = package_util.get_task_type_cls()
52
51
  # If no arguments are specified, generate both input and output files.
53
52
  if not self.ins and not self.outs:
54
53
  self.ins = True
@@ -58,6 +57,10 @@ class Command(BaseCommand):
58
57
  command = IngenCommand()
59
58
  command.run(args)
60
59
 
60
+ if not self.task_type.run_outgen():
61
+ print(util.warning("Outgen is not supported for this task type."))
62
+ return
63
+
61
64
  if self.outs:
62
65
  command = OutgenCommand()
63
66
  command.run(args)
@@ -30,22 +30,43 @@ class Command(BaseCommand):
30
30
  help='path to ingen source file, for example prog/abcingen.cpp')
31
31
  parser.add_argument('-n', '--no-validate', default=False, action='store_true',
32
32
  help='do not validate test contents')
33
- parser.add_argument('-f', '--fsanitize', default=False, action='store_true',
34
- help='Use -fsanitize=address,undefined for compilation. Warning: this may fail on some '
35
- 'systems. Tof fix this, run `sudo sysctl vm.mmap_rnd_bits = 28`.')
33
+ parsers.add_cpus_argument(parser, 'number of cpus used for validating tests')
34
+ parsers.add_fsanitize_argument(parser)
36
35
  parsers.add_compilation_arguments(parser)
37
36
  return parser
38
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
+
39
61
  def run(self, args: argparse.Namespace):
40
62
  args = util.init_package_command(args)
41
63
 
42
64
  self.args = args
43
65
 
44
66
  self.task_id = package_util.get_task_id()
45
- package_util.validate_test_names(self.task_id)
46
67
  util.change_stack_size_to_unlimited()
47
68
  self.ingen = get_ingen(self.task_id, args.ingen_path)
48
- print(util.info(f'Using ingen file {os.path.basename(self.ingen)}'))
69
+ print(f'Using ingen file {os.path.basename(self.ingen)}')
49
70
  self.ingen_exe = compile_ingen(self.ingen, self.args, self.args.compile_mode, self.args.fsanitize)
50
71
 
51
72
  previous_tests = []
@@ -64,18 +85,11 @@ class Command(BaseCommand):
64
85
  else:
65
86
  util.exit_with_error('Failed to generate input files.')
66
87
 
67
- print(util.info('Cleaning up old input files.'))
68
- for test in glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")):
69
- basename = os.path.basename(test)
70
- if basename in dates and dates[basename] == os.path.getmtime(test):
71
- os.unlink(test)
88
+ self.delete_dangling_files(dates)
72
89
 
73
90
  with open(paths.get_cache_path("input_tests"), "w") as f:
74
91
  f.write("\n".join(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in"))))
75
92
 
76
93
  if not self.args.no_validate:
77
- print(util.info('Validating input test contents.'))
78
94
  tests = sorted(glob.glob(os.path.join(os.getcwd(), "in", f"{self.task_id}*.in")))
79
- for test in tests:
80
- package_util.validate_test(test)
81
- print(util.info('Input test contents are valid!'))
95
+ package_util.validate_tests(tests, self.args.cpus, 'input')
@@ -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)