sinol-make 1.7.0.dev1__tar.gz → 1.8.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 (72) hide show
  1. {sinol_make-1.7.0.dev1/src/sinol_make.egg-info → sinol_make-1.8.0}/PKG-INFO +35 -6
  2. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/README.md +34 -5
  3. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/setup.cfg +1 -1
  4. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/__init__.py +28 -15
  5. sinol_make-1.8.0/src/sinol_make/commands/chkwer/__init__.py +137 -0
  6. sinol_make-1.8.0/src/sinol_make/commands/chkwer/chkwer_util.py +72 -0
  7. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/doc/__init__.py +6 -2
  8. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/export/__init__.py +8 -2
  9. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/gen/__init__.py +10 -4
  10. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/ingen/ingen_util.py +2 -2
  11. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/init/__init__.py +26 -10
  12. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/inwer/__init__.py +8 -14
  13. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/inwer/inwer_util.py +5 -1
  14. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/outgen/__init__.py +3 -4
  15. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/run/__init__.py +109 -93
  16. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/verify/__init__.py +5 -0
  17. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/contest_types/__init__.py +2 -0
  18. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/contest_types/default.py +18 -0
  19. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/contest_types/icpc.py +6 -0
  20. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/contest_types/oi.py +6 -0
  21. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/contest_types/oij.py +3 -0
  22. sinol_make-1.8.0/src/sinol_make/executors/__init__.py +65 -0
  23. sinol_make-1.8.0/src/sinol_make/executors/detailed.py +91 -0
  24. sinol_make-1.8.0/src/sinol_make/executors/sio2jail.py +91 -0
  25. sinol_make-1.8.0/src/sinol_make/executors/time.py +113 -0
  26. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/cache.py +3 -3
  27. sinol_make-1.8.0/src/sinol_make/helpers/classinit.py +90 -0
  28. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/compile.py +22 -11
  29. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/compiler.py +2 -2
  30. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/package_util.py +21 -4
  31. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/paths.py +8 -0
  32. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/interfaces/BaseCommand.py +7 -0
  33. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/interfaces/Errors.py +1 -1
  34. sinol_make-1.8.0/src/sinol_make/sio2jail/__init__.py +102 -0
  35. sinol_make-1.8.0/src/sinol_make/structs/chkwer_structs.py +68 -0
  36. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/run_structs.py +0 -2
  37. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/status_structs.py +27 -6
  38. sinol_make-1.8.0/src/sinol_make/task_type/__init__.py +179 -0
  39. sinol_make-1.8.0/src/sinol_make/task_type/interactive.py +238 -0
  40. sinol_make-1.8.0/src/sinol_make/task_type/normal.py +37 -0
  41. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/util.py +1 -0
  42. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0/src/sinol_make.egg-info}/PKG-INFO +35 -6
  43. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make.egg-info/SOURCES.txt +12 -6
  44. sinol_make-1.8.0/tests/test_sio2jail.py +104 -0
  45. sinol_make-1.7.0.dev1/src/sinol_make/oiejq/__init__.py +0 -128
  46. sinol_make-1.7.0.dev1/src/sinol_make/structs/package_structs.py +0 -7
  47. sinol_make-1.7.0.dev1/src/sinol_make/task_type/__init__.py +0 -17
  48. sinol_make-1.7.0.dev1/src/sinol_make/task_type/base.py +0 -273
  49. sinol_make-1.7.0.dev1/src/sinol_make/task_type/interactive_io.py +0 -219
  50. sinol_make-1.7.0.dev1/src/sinol_make/task_type/normal.py +0 -77
  51. sinol_make-1.7.0.dev1/tests/test_oiejq.py +0 -120
  52. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/LICENSE +0 -0
  53. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/pyproject.toml +0 -0
  54. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/ingen/__init__.py +0 -0
  55. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/commands/outgen/outgen_util.py +0 -0
  56. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/__init__.py +0 -0
  57. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/func_cache.py +0 -0
  58. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/parsers.py +0 -0
  59. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/helpers/printer.py +0 -0
  60. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/interfaces/__init__.py +0 -0
  61. {sinol_make-1.7.0.dev1/src/sinol_make/oiejq → sinol_make-1.8.0/src/sinol_make/sio2jail}/perf_test.py +0 -0
  62. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/__init__.py +0 -0
  63. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/cache_structs.py +0 -0
  64. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/compiler_structs.py +0 -0
  65. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/gen_structs.py +0 -0
  66. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make/structs/inwer_structs.py +0 -0
  67. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make.egg-info/dependency_links.txt +0 -0
  68. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make.egg-info/entry_points.txt +0 -0
  69. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make.egg-info/requires.txt +0 -0
  70. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/src/sinol_make.egg-info/top_level.txt +0 -0
  71. {sinol_make-1.7.0.dev1 → sinol_make-1.8.0}/tests/test_multiple_arguments.py +0 -0
  72. {sinol_make-1.7.0.dev1 → sinol_make-1.8.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.7.0.dev1
3
+ Version: 1.8.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?
@@ -57,7 +58,7 @@ they lack some built-in mechanisms for verifying packages and finding mistakes
57
58
  before uploading the package to the judge system.
58
59
  As sinol-make was created specifically for the sio2 problem packages,
59
60
  by default it downloads and uses sio2's deterministic mechanism of measuring
60
- solutions' runtime, called `oiejq`.
61
+ solutions' runtime, called `sio2jail`.
61
62
 
62
63
  ### Installation
63
64
 
@@ -72,7 +73,7 @@ pip3 install sinol-make
72
73
  so make sure this directory is in your `PATH`.
73
74
  [Here's](https://gist.github.com/nex3/c395b2f8fd4b02068be37c961301caa7) how to add a directory to `PATH`.
74
75
 
75
- As `oiejq` works only on Linux-based operating systems,
76
+ As `sio2jail` works only on Linux-based operating systems,
76
77
  *we do not recommend* using operating systems such as Windows or macOS.
77
78
  Nevertheless `sinol-make` supports those operating systems,
78
79
  though there are additional installation steps required to use
@@ -93,11 +94,11 @@ 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
- of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
100
- compares the solutions' scores with the ones saved in config.yml. If you're using oiejq, make sure you are not running on efficiency
100
+ of CPUs. Measures the solutions' time with sio2jail, unless specified otherwise. After running the solutions, it
101
+ compares the solutions' scores with the ones saved in config.yml. If you're using sio2jail, make sure you are not running on efficiency
101
102
  cpu cores. You can check if you have them [like this](https://stackoverflow.com/a/71282744). To run on normal cpu cores, use
102
103
  `taskset -c 8-15 sinol-make ...`, assuming that cpu cores 8-15 are not efficiency cores.
103
104
  Run `sinol-make run --help` to see available flags.
@@ -116,6 +117,8 @@ program to use, what tests to check and how many CPUs to use. Run `sinol-make in
116
117
  - `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
117
118
  generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
118
119
  address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
120
+ - `sinol-make chkwer` -- Run checker with model solution and print results. Prints a table with points and checker's comments.
121
+ This command fails if the model solution didn't score maximum points. Run `sinol-make chkwer --help` to see all available flags.
119
122
  - `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.
120
123
 
121
124
  You can also run multiple commands at once, for example:
@@ -124,6 +127,32 @@ You can also run multiple commands at once, for example:
124
127
  sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
125
128
  ```
126
129
 
130
+ There are also available short aliases for the commands:
131
+ - `sinol-make r` for `sinol-make run`
132
+ - `sinol-make g` for `sinol-make gen`
133
+ - `sinol-make i` for `sinol-make inwer`
134
+ - `sinol-make e` for `sinol-make export`
135
+ - `sinol-make d` for `sinol-make doc`
136
+ - `sinol-make v` for `sinol-make verify`
137
+ - `sinol-make c` for `sinol-make chkwer`
138
+ - `sm` for `sinol-make`
139
+
140
+ ### Contest types
141
+
142
+ `sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
143
+ the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
144
+ features:
145
+
146
+ | Feature | `default` | `oi` | `oij` | `icpc` |
147
+ |-----------------------------------------------------------------------------------------------------|-----------|----------|----------|--------|
148
+ | Max score | 100 | 100 | 100 | 1 |
149
+ | Default time tool | sio2jail | sio2jail | sio2jail | time |
150
+ | Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
151
+ | Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
152
+ | Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
153
+ | Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
154
+ | Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
155
+
127
156
  ### Reporting bugs and contributing code
128
157
 
129
158
  - 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?
@@ -27,7 +28,7 @@ they lack some built-in mechanisms for verifying packages and finding mistakes
27
28
  before uploading the package to the judge system.
28
29
  As sinol-make was created specifically for the sio2 problem packages,
29
30
  by default it downloads and uses sio2's deterministic mechanism of measuring
30
- solutions' runtime, called `oiejq`.
31
+ solutions' runtime, called `sio2jail`.
31
32
 
32
33
  ### Installation
33
34
 
@@ -42,7 +43,7 @@ pip3 install sinol-make
42
43
  so make sure this directory is in your `PATH`.
43
44
  [Here's](https://gist.github.com/nex3/c395b2f8fd4b02068be37c961301caa7) how to add a directory to `PATH`.
44
45
 
45
- As `oiejq` works only on Linux-based operating systems,
46
+ As `sio2jail` works only on Linux-based operating systems,
46
47
  *we do not recommend* using operating systems such as Windows or macOS.
47
48
  Nevertheless `sinol-make` supports those operating systems,
48
49
  though there are additional installation steps required to use
@@ -63,11 +64,11 @@ 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
- of CPUs. Measures the solutions' time with oiejq, unless specified otherwise. After running the solutions, it
70
- compares the solutions' scores with the ones saved in config.yml. If you're using oiejq, make sure you are not running on efficiency
70
+ of CPUs. Measures the solutions' time with sio2jail, unless specified otherwise. After running the solutions, it
71
+ compares the solutions' scores with the ones saved in config.yml. If you're using sio2jail, make sure you are not running on efficiency
71
72
  cpu cores. You can check if you have them [like this](https://stackoverflow.com/a/71282744). To run on normal cpu cores, use
72
73
  `taskset -c 8-15 sinol-make ...`, assuming that cpu cores 8-15 are not efficiency cores.
73
74
  Run `sinol-make run --help` to see available flags.
@@ -86,6 +87,8 @@ program to use, what tests to check and how many CPUs to use. Run `sinol-make in
86
87
  - `sinol-make verify` -- Verifies the package. This command runs stress tests (if available), verifies the config,
87
88
  generates tests, generates problem statements, runs inwer and run all solutions. Ingen and inwer are compiled with
88
89
  address and UB sanitizers. Run `sinol-make verify --help` to see all available flags.
90
+ - `sinol-make chkwer` -- Run checker with model solution and print results. Prints a table with points and checker's comments.
91
+ This command fails if the model solution didn't score maximum points. Run `sinol-make chkwer --help` to see all available flags.
89
92
  - `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.
90
93
 
91
94
  You can also run multiple commands at once, for example:
@@ -94,6 +97,32 @@ You can also run multiple commands at once, for example:
94
97
  sinol-make gen prog/abcingen2.cpp inwer --cpus 4 run --tests abc1*.in doc export --no-statement
95
98
  ```
96
99
 
100
+ There are also available short aliases for the commands:
101
+ - `sinol-make r` for `sinol-make run`
102
+ - `sinol-make g` for `sinol-make gen`
103
+ - `sinol-make i` for `sinol-make inwer`
104
+ - `sinol-make e` for `sinol-make export`
105
+ - `sinol-make d` for `sinol-make doc`
106
+ - `sinol-make v` for `sinol-make verify`
107
+ - `sinol-make c` for `sinol-make chkwer`
108
+ - `sm` for `sinol-make`
109
+
110
+ ### Contest types
111
+
112
+ `sinol-make` changes its behavior depending on the contest type specified in `config.yml`. You can specify
113
+ the contest type with the `sinol_contest_type` key in config. Here is the table of available contest types and their
114
+ features:
115
+
116
+ | Feature | `default` | `oi` | `oij` | `icpc` |
117
+ |-----------------------------------------------------------------------------------------------------|-----------|----------|----------|--------|
118
+ | Max score | 100 | 100 | 100 | 1 |
119
+ | Default time tool | sio2jail | sio2jail | sio2jail | time |
120
+ | Full score if took less than half of the time limit, <br/>otherwise linearly decreasing to 1. | ❌ | ✔️ | ❌ | ❌ |
121
+ | Full score if took less than the time limit | ✔️ | ❌ | ✔️ | ✔️ |
122
+ | Scores must sum up to 100 | ❌ | ✔️ | ✔️ | ❌ |
123
+ | Limits can be set for individual tests | ✔️ | ❌ | ✔️ | ✔️ |
124
+ | Verifies if tests are named correctly<br/> (letters within groups increase, group numbers increase) | ❌ | ✔️ | ✔️ | ✔️ |
125
+
97
126
  ### Reporting bugs and contributing code
98
127
 
99
128
  - Want to report a bug or request a feature? [Open an issue](https://github.com/sio2project/sinol-make/issues).
@@ -51,7 +51,7 @@ testpaths =
51
51
  tests
52
52
  markers =
53
53
  github_runner: Mark tests that require GitHub runner
54
- oiejq: Mark tests that require working oiejq
54
+ sio2jail: Mark tests that require working sio2jail
55
55
 
56
56
  [egg_info]
57
57
  tag_build =
@@ -4,10 +4,14 @@ import argparse
4
4
  import traceback
5
5
  import argcomplete
6
6
 
7
- from sinol_make import util, oiejq
7
+ from sinol_make import util, sio2jail
8
8
 
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
9
12
 
10
- __version__ = "1.7.0.dev1"
13
+
14
+ __version__ = "1.8.0"
11
15
 
12
16
 
13
17
  def configure_parsers():
@@ -32,19 +36,19 @@ def configure_parsers():
32
36
  return parser
33
37
 
34
38
 
35
- def check_oiejq():
36
- if util.is_linux() and not oiejq.check_oiejq():
37
- print(util.warning('Up to date `oiejq` in `~/.local/bin/` not found, installing new version...'))
39
+ def check_sio2jail():
40
+ if util.is_linux() and not sio2jail.check_sio2jail():
41
+ print(util.warning('Up to date `sio2jail` in `~/.local/bin/` not found, installing new version...'))
38
42
  try:
39
- if oiejq.install_oiejq():
40
- print(util.info('`oiejq` was successfully installed.'))
43
+ if sio2jail.install_sio2jail():
44
+ print(util.info('Newest `sio2jail` was successfully installed.'))
41
45
  else:
42
- util.exit_with_error('`oiejq` could not be installed.\n'
43
- 'You can download it from https://oij.edu.pl/zawodnik/srodowisko/oiejq.tar.gz, '
44
- 'unpack it to `~/.local/bin/` and rename oiejq.sh to oiejq.\n'
45
- 'You can also use --oiejq-path to specify path to your oiejq.')
46
+ util.exit_with_error('`sio2jail` could not be installed.\n'
47
+ 'You can download it from https://oij.edu.pl/zawodnik/srodowisko/oiejq.tar.gz '
48
+ 'and unpack it to `~/.local/bin/`.\n'
49
+ 'You can also use --sio2jail-path to specify path to your sio2jail.')
46
50
  except Exception as err:
47
- util.exit_with_error('`oiejq` could not be installed.\n' + str(err))
51
+ util.exit_with_error('`sio2jail` could not be installed.\n' + str(err))
48
52
 
49
53
 
50
54
  def main_exn():
@@ -53,11 +57,16 @@ def main_exn():
53
57
  curr_args = []
54
58
  commands = util.get_commands()
55
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()}
56
61
  for arg in sys.argv[1:]:
57
62
  if arg in commands_dict.keys() and not (len(curr_args) > 0 and curr_args[0] == 'init'):
58
63
  if curr_args:
59
64
  arguments.append(curr_args)
60
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()]
61
70
  else:
62
71
  curr_args.append(arg)
63
72
  if curr_args:
@@ -65,7 +74,7 @@ def main_exn():
65
74
  if not arguments:
66
75
  parser.print_help()
67
76
  exit(1)
68
- check_oiejq()
77
+ check_sio2jail()
69
78
 
70
79
  for curr_args in arguments:
71
80
  args = parser.parse_args(curr_args)
@@ -101,9 +110,13 @@ def main():
101
110
  if not util.is_dev(new_version):
102
111
  print(util.warning(
103
112
  f'New version of sinol-make is available (your version: {__version__}, available version: '
104
- f'{new_version}).\nYou can update it by running `pip3 install sinol-make --upgrade`.'))
113
+ f'{new_version}).\n'
114
+ f'Changelog can be found at https://github.com/sio2project/sinol-make/releases.\n'
115
+ f'You can update sinol-make by running `pip install sinol-make --upgrade`.'))
105
116
  elif util.is_dev(new_version):
106
117
  print(util.warning(
107
118
  f'New development version of sinol-make is available (your version: {__version__}, available '
108
- f'version: {new_version}).\nYou can update it by running `pip3 install sinol-make --pre --upgrade`.'
119
+ f'version: {new_version}).\n'
120
+ f'Changelog can be found at https://github.com/sio2project/sinol-make/releases.\n'
121
+ f'You can update sinol-make by running `pip install sinol-make --pre --upgrade`.'
109
122
  ))
@@ -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."
@@ -12,11 +12,14 @@ class Command(BaseCommand):
12
12
  """
13
13
  Class for `doc` command.
14
14
  """
15
- LOG_PATTERNS = ['*~', '*.aux', '*.log', '*.dvi', '*.err', '*.inf']
15
+ LOG_PATTERNS = ['*~', '*.aux', '*.log', '*.dvi', '*.err', '*.inf', '*.out']
16
16
 
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))
@@ -37,7 +40,8 @@ class Command(BaseCommand):
37
40
  def compile_pdf_latex(self, file_path):
38
41
  print(f'Compiling {os.path.basename(file_path)} (pdflatex)...')
39
42
  os.chdir(os.path.dirname(file_path))
40
- subprocess.run(['pdflatex', file_path])
43
+ for _ in range(3):
44
+ subprocess.run(['pdflatex', file_path])
41
45
  pdf_file = os.path.splitext(file_path)[0] + '.pdf'
42
46
  pdf_file_path = os.path.join(os.path.dirname(file_path), pdf_file)
43
47
  if not os.path.exists(pdf_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(),
@@ -215,12 +218,15 @@ class Command(BaseCommand):
215
218
  if 'c' in config['extra_compilation_args']:
216
219
  c_flags += ' ' + format_multiple_arguments(config['extra_compilation_args']['c'])
217
220
 
221
+ tl = config.get('time_limit', None)
222
+ if not tl:
223
+ tl = config['time_limits'][0]
218
224
  f.write(f'MODE = wer\n'
219
225
  f'ID = {self.task_id}\n'
220
226
  f'SIG = sinolmake\n'
221
227
  f'\n'
222
- f'TIMELIMIT = {config["time_limit"]}\n'
223
- f'SLOW_TIMELIMIT = {4 * config["time_limit"]}\n'
228
+ f'TIMELIMIT = {tl}\n'
229
+ f'SLOW_TIMELIMIT = {4 * tl}\n'
224
230
  f'MEMLIMIT = {config["memory_limit"]}\n'
225
231
  f'\n'
226
232
  f'OI_TIME = oiejq\n'
@@ -3,9 +3,8 @@ 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
- from sinol_make.task_type import get_task_type
9
8
 
10
9
 
11
10
  class Command(BaseCommand):
@@ -16,6 +15,9 @@ class Command(BaseCommand):
16
15
  def get_name(self):
17
16
  return "gen"
18
17
 
18
+ def get_short_name(self):
19
+ return "g"
20
+
19
21
  def configure_subparser(self, subparser):
20
22
  parser = subparser.add_parser(
21
23
  self.get_name(),
@@ -41,11 +43,11 @@ class Command(BaseCommand):
41
43
 
42
44
  def run(self, args: argparse.Namespace):
43
45
  args = util.init_package_command(args)
44
- task_type = get_task_type()
45
46
 
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
 
58
- if self.outs and task_type.run_outgen():
60
+ if not self.task_type.run_outgen():
61
+ print(util.warning("Outgen is not supported for this task type."))
62
+ return
63
+
64
+ if self.outs:
59
65
  command = OutgenCommand()
60
66
  command.run(args)
@@ -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)
@@ -100,7 +100,7 @@ def run_ingen(ingen_exe, working_dir=None):
100
100
 
101
101
  if util.has_sanitizer_error(whole_output, exit_code):
102
102
  print(util.warning('Warning: if ingen failed due to sanitizer errors, you can either run '
103
- '`sudo sysctl vm.mmap_rnd_bits = 28` to fix this or disable sanitizers with the '
103
+ '`sudo sysctl vm.mmap_rnd_bits=28` to fix this or disable sanitizers with the '
104
104
  '--no-fsanitize flag.'))
105
105
 
106
106
  return exit_code == 0