rbx.cp 0.5.46__tar.gz → 0.5.47__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 (194) hide show
  1. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/PKG-INFO +1 -1
  2. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/pyproject.toml +1 -1
  3. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/checkers.py +81 -28
  4. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/cli.py +12 -10
  5. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/boca/packager.py +44 -7
  6. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/main.py +7 -0
  7. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/moj/packager.py +88 -8
  8. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/packager.py +7 -2
  9. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/packager.py +5 -4
  10. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/solutions.py +2 -2
  11. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/stresses.py +0 -1
  12. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/tasks.py +6 -4
  13. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/sandbox.py +29 -1
  14. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/isolate.py +10 -0
  15. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/stupid_sandbox.py +16 -4
  16. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/timeit.py +12 -3
  17. rbx_cp-0.5.47/rbx/grading/processing_context.py +48 -0
  18. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/steps.py +24 -13
  19. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/checker.sh +8 -6
  20. rbx_cp-0.5.47/rbx/resources/packagers/boca/compare.sh +48 -0
  21. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/c +207 -0
  22. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/cc +207 -0
  23. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/cpp +207 -0
  24. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/java +240 -0
  25. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/kt +231 -0
  26. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/py2 +209 -0
  27. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/py3 +209 -0
  28. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactor_compile.sh +45 -0
  29. rbx_cp-0.5.47/rbx/resources/packagers/boca/run/bkp +163 -0
  30. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/c +19 -19
  31. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/cc +19 -19
  32. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/cpp +19 -19
  33. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/java +51 -51
  34. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/kt +30 -30
  35. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/py2 +42 -42
  36. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/py3 +42 -42
  37. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/compile.sh +19 -0
  38. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/prep.sh +5 -0
  39. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/run.sh +16 -0
  40. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/moj/scripts/compare.sh +32 -6
  41. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/compile.sh +19 -0
  42. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/prep.sh +5 -0
  43. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/run.sh +16 -0
  44. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/interactor_prep.sh +14 -0
  45. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/interactor_run.sh +38 -0
  46. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/compile.sh +12 -0
  47. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/prep.sh +8 -0
  48. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/run.sh +17 -0
  49. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/compile.sh +7 -0
  50. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/prep.sh +5 -0
  51. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/run.sh +16 -0
  52. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/compile.sh +7 -0
  53. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/prep.sh +5 -0
  54. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/run.sh +16 -0
  55. rbx_cp-0.5.46/rbx/resources/packagers/boca/compare +0 -53
  56. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/LICENSE +0 -0
  57. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/README.md +0 -0
  58. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/__init__.py +0 -0
  59. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/annotations.py +0 -0
  60. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/autoenum.py +0 -0
  61. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/__init__.py +0 -0
  62. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/builder.py +0 -0
  63. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/cd.py +0 -0
  64. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/code.py +0 -0
  65. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/compile.py +0 -0
  66. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/conftest.py +0 -0
  67. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/__init__.py +0 -0
  68. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/build_contest_statements.py +0 -0
  69. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/contest_package.py +0 -0
  70. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/contest_utils.py +0 -0
  71. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/main.py +0 -0
  72. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/schema.py +0 -0
  73. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/contest/statements.py +0 -0
  74. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/creation.py +0 -0
  75. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/deferred.py +0 -0
  76. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/download.py +0 -0
  77. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/environment.py +0 -0
  78. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/extensions.py +0 -0
  79. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/formatting.py +0 -0
  80. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/generators.py +0 -0
  81. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/generators_test.py +0 -0
  82. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/lazy_importing_main.py +0 -0
  83. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/lazy_importing_test.py +0 -0
  84. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/main.py +0 -0
  85. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/package.py +0 -0
  86. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/boca/extension.py +0 -0
  87. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/contest_main.py +0 -0
  88. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/test.py +0 -0
  89. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  90. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/presets/__init__.py +0 -0
  91. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/presets/fetch.py +0 -0
  92. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/presets/lock_schema.py +0 -0
  93. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/presets/schema.py +0 -0
  94. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/retries.py +0 -0
  95. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/sanitizers/warning_stack.py +0 -0
  96. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/schema.py +0 -0
  97. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/setter_config.py +0 -0
  98. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/solutions_test.py +0 -0
  99. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/state.py +0 -0
  100. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/__init__.py +0 -0
  101. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/build_statements.py +0 -0
  102. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/builders.py +0 -0
  103. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/joiners.py +0 -0
  104. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/latex.py +0 -0
  105. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/latex_jinja.py +0 -0
  106. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/statements/schema.py +0 -0
  107. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/stressing/__init__.py +0 -0
  108. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/stressing/finder_parser.py +0 -0
  109. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/stressing/generator_parser.py +0 -0
  110. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/testcase_extractors.py +0 -0
  111. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/testcase_utils.py +0 -0
  112. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/testcases/__init__.py +0 -0
  113. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/testcases/main.py +0 -0
  114. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/ui/__init__.py +0 -0
  115. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/ui/captured_log.py +0 -0
  116. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/ui/css/app.tcss +0 -0
  117. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/ui/main.py +0 -0
  118. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/ui/run.py +0 -0
  119. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/unit.py +0 -0
  120. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/validators.py +0 -0
  121. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/box/validators_test.py +0 -0
  122. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/checker.py +0 -0
  123. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/clone.py +0 -0
  124. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/config.py +0 -0
  125. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/conftest.py +0 -0
  126. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/console.py +0 -0
  127. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/create.py +0 -0
  128. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/edit.py +0 -0
  129. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/__init__.py +0 -0
  130. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/caching.py +0 -0
  131. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/conftest.py +0 -0
  132. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/__init__.py +0 -0
  133. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/cacher.py +0 -0
  134. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/digester.py +0 -0
  135. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  136. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/storage.py +0 -0
  137. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/test.py +0 -0
  138. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/judge/testiso.py +0 -0
  139. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/steps_with_caching.py +0 -0
  140. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading/steps_with_caching_run_test.py +0 -0
  141. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/grading_utils.py +0 -0
  142. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/hydration.py +0 -0
  143. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/main.py +0 -0
  144. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/metadata.py +0 -0
  145. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/providers/__init__.py +0 -0
  146. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/providers/codeforces.py +0 -0
  147. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/providers/provider.py +0 -0
  148. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/checkers/boilerplate.cpp +0 -0
  149. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/default_config.json +0 -0
  150. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/default_setter_config.mac.yml +0 -0
  151. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/default_setter_config.yml +0 -0
  152. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/envs/default.rbx.yml +0 -0
  153. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/envs/isolate.rbx.yml +0 -0
  154. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/c +0 -0
  155. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/cc +0 -0
  156. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/cpp +0 -0
  157. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/java +0 -0
  158. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/kt +0 -0
  159. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/pas +0 -0
  160. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/py2 +0 -0
  161. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/py3 +0 -0
  162. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  163. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  164. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  165. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  166. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  167. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/.gitignore +0 -0
  168. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  169. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  170. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/random.py +0 -0
  171. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/random.txt +0 -0
  172. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  173. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  174. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  175. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  176. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  177. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  178. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  179. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  180. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  181. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  182. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  183. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/resources/templates/template.cpp +0 -0
  184. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/run.py +0 -0
  185. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/schema.py +0 -0
  186. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/submit.py +0 -0
  187. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/submitors/__init__.py +0 -0
  188. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/submitors/codeforces.py +0 -0
  189. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/submitors/submitor.py +0 -0
  190. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/test.py +0 -0
  191. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/testcase.py +0 -0
  192. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/testcase_rendering.py +0 -0
  193. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/testing_utils.py +0 -0
  194. {rbx_cp-0.5.46 → rbx_cp-0.5.47}/rbx/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.5.46
3
+ Version: 0.5.47
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9,<4.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.5.46"
3
+ version = "0.5.47"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -1,4 +1,5 @@
1
1
  import pathlib
2
+ import signal
2
3
  from typing import Optional
3
4
 
4
5
  import typer
@@ -108,23 +109,31 @@ def _convert_tle(result: CheckerResult, run_log: Optional[RunLog]) -> CheckerRes
108
109
  return result
109
110
 
110
111
 
112
+ def _is_checker_exitcode(exitcode: int) -> bool:
113
+ return exitcode in [0, 1, 2, 3]
114
+
115
+
111
116
  def process_checker_run_log(
112
117
  checker_run_log: Optional[RunLog], message: str
113
- ) -> Optional[CheckerResult]:
118
+ ) -> CheckerResult:
114
119
  if (
115
120
  checker_run_log is not None
116
- and checker_run_log.exitcode != 0
117
- and (
118
- checker_run_log.exitstatus != SandboxBase.EXIT_NONZERO_RETURN
119
- or checker_run_log.exitcode not in [0, 1, 2, 3]
120
- )
121
+ and checker_run_log.exitstatus == SandboxBase.EXIT_SANDBOX_ERROR
121
122
  ):
122
- return None
123
+ # When the sandbox fails, it means the checker failed to run.
124
+ # We don't know what happened.
125
+ return CheckerResult(
126
+ outcome=Outcome.INTERNAL_ERROR,
127
+ message='sandbox failed to run checker',
128
+ )
123
129
 
124
130
  if checker_run_log is None:
125
131
  return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
126
- if checker_run_log.exitcode not in [0, 1, 2, 3]:
127
- return None
132
+ if not _is_checker_exitcode(checker_run_log.exitcode):
133
+ return CheckerResult(
134
+ outcome=Outcome.JUDGE_FAILED,
135
+ message=f'checker failed with unknown exit code {checker_run_log.exitcode}: {message}',
136
+ )
128
137
 
129
138
  result = CheckerResult(outcome=Outcome.ACCEPTED, message=message)
130
139
 
@@ -185,7 +194,7 @@ async def _check(
185
194
  message = package.get_digest_as_string(error.value or '') or ''
186
195
 
187
196
  processed_checker_result = process_checker_run_log(checker_run_log, message)
188
- if processed_checker_result is None:
197
+ if processed_checker_result.outcome == Outcome.INTERNAL_ERROR:
189
198
  console.console.print(
190
199
  f'[error]Checker [item]{package.get_checker().path}[/item] failed unexpectedly.[/error]'
191
200
  )
@@ -240,33 +249,77 @@ async def check_communication(
240
249
  program_output: pathlib.Path,
241
250
  skip_run_log: bool = False,
242
251
  ) -> CheckerResult:
243
- sanitizer_warnings = _check_sanitizer_warnings(run_log)
252
+ def _extra_check_and_sanitize(result: CheckerResult) -> CheckerResult:
253
+ result.sanitizer_warnings = _check_sanitizer_warnings(run_log)
254
+ return result
255
+
256
+ def _check_interactor(reinterpret_rte: bool = True) -> Optional[CheckerResult]:
257
+ result = process_checker_run_log(
258
+ interactor_run_log, interactor_stderr.read_text()
259
+ )
260
+ if result.outcome in [Outcome.JUDGE_FAILED, Outcome.WRONG_ANSWER]:
261
+ # Only return testlib errors (exit code 2 and 3), skip other types of RTEs and verdicts.
262
+ if (
263
+ interactor_run_log is not None
264
+ and _is_checker_exitcode(interactor_run_log.exitcode)
265
+ and interactor_run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
266
+ ):
267
+ return _extra_check_and_sanitize(result)
268
+ else:
269
+ # Check for other verdicts, but potentially reinterpret RTEs as JUDGE_FAILED.
270
+ result = check_with_no_output(interactor_run_log)
271
+ if result.outcome == Outcome.RUNTIME_ERROR and reinterpret_rte:
272
+ result.outcome = Outcome.JUDGE_FAILED
273
+ if result.outcome != Outcome.ACCEPTED:
274
+ return _extra_check_and_sanitize(result)
275
+ else:
276
+ # Return any other checker/interactor errors, such as INTERNAL_ERRORs.
277
+ return _extra_check_and_sanitize(result)
278
+
279
+ # No relevant error was found.
280
+ return None
244
281
 
282
+ # 1. If the solution received SIGPIPE or was terminated, it means the
283
+ # interactor exited before it. Thus, check the interactor, as it might have
284
+ # returned a checker verdict.
285
+ #
286
+ # Also, do the same if the solution returned a non-zero exit code. Checker verdict
287
+ # should usually supersede a solution's RTE verdict.
288
+ if (
289
+ interactor_run_log is not None
290
+ and run_log is not None
291
+ and (
292
+ run_log.exitcode == -signal.SIGPIPE
293
+ or run_log.exitstatus == SandboxBase.EXIT_TERMINATED
294
+ or run_log.exitstatus == SandboxBase.EXIT_NONZERO_RETURN
295
+ )
296
+ ):
297
+ result = _check_interactor()
298
+ if result is not None and result.outcome != Outcome.ACCEPTED:
299
+ return _extra_check_and_sanitize(result)
300
+
301
+ # 2. Check if the solution failed without looking at its output (TLE, MLE, RTE, etc).
245
302
  result = check_with_no_output(run_log)
246
- result.sanitizer_warnings = sanitizer_warnings
247
303
  if result.outcome != Outcome.ACCEPTED:
248
- return result
304
+ return _extra_check_and_sanitize(result)
249
305
 
250
- result = process_checker_run_log(
251
- interactor_run_log,
252
- interactor_stderr.read_text(),
253
- )
254
-
255
- if result is None:
256
- result = check_with_no_output(interactor_run_log)
257
- result.sanitizer_warnings = sanitizer_warnings
258
- if result.outcome != Outcome.ACCEPTED:
259
- result.outcome = Outcome.JUDGE_FAILED
260
- return result
306
+ # 3. Now check interactor return code regardless of what happened to the
307
+ # solution.
308
+ result = _check_interactor()
309
+ if result is not None and result.outcome != Outcome.ACCEPTED:
310
+ return _extra_check_and_sanitize(result)
261
311
 
262
- result.sanitizer_warnings = sanitizer_warnings
312
+ # Just a defensive pattern to ensure result is not None, should never happen.
313
+ result = check_with_no_output(interactor_run_log)
263
314
  if result.outcome != Outcome.ACCEPTED:
264
- return result
315
+ if result.outcome == Outcome.RUNTIME_ERROR:
316
+ result.outcome = Outcome.JUDGE_FAILED
317
+ return _extra_check_and_sanitize(result)
265
318
 
319
+ # 4. Now actually check the output with a checker.
266
320
  if checker_digest is not None:
267
321
  result = await check(
268
322
  checker_digest, run_log, testcase, program_output, skip_run_log
269
323
  )
270
- result.sanitizer_warnings = sanitizer_warnings
271
324
 
272
- return result
325
+ return _extra_check_and_sanitize(result)
@@ -222,7 +222,7 @@ async def run(
222
222
  tracked_solutions = {solution}
223
223
 
224
224
  if choice:
225
- tracked_solutions = set(pick_solutions(tracked_solutions))
225
+ tracked_solutions = set(await pick_solutions(tracked_solutions))
226
226
  if not tracked_solutions:
227
227
  console.console.print('[error]No solutions selected. Exiting.[/error]')
228
228
  raise typer.Exit(1)
@@ -453,7 +453,7 @@ async def irun(
453
453
  tracked_solutions = {solution}
454
454
 
455
455
  if choice:
456
- tracked_solutions = set(pick_solutions(tracked_solutions))
456
+ tracked_solutions = set(await pick_solutions(tracked_solutions))
457
457
  if not tracked_solutions:
458
458
  console.console.print('[error]No solutions selected. Exiting.[/error]')
459
459
  raise typer.Exit(1)
@@ -515,7 +515,8 @@ def create(
515
515
  help='Run a stress test.',
516
516
  )
517
517
  @package.within_problem
518
- def stress(
518
+ @syncer.sync
519
+ async def stress(
519
520
  name: Annotated[
520
521
  str,
521
522
  typer.Argument(
@@ -574,7 +575,7 @@ def stress(
574
575
  from rbx.box import stresses
575
576
 
576
577
  with utils.StatusProgress('Running stress...') as s:
577
- report = stresses.run_stress(
578
+ report = await stresses.run_stress(
578
579
  name,
579
580
  timeout,
580
581
  args=generator_args,
@@ -608,15 +609,15 @@ def stress(
608
609
 
609
610
  import questionary
610
611
 
611
- testgroup = questionary.select(
612
+ testgroup = await questionary.select(
612
613
  'Choose the testgroup to add the tests to.\nOnly test groups that have a .txt generatorScript are shown below: ',
613
614
  choices=list(groups_by_name) + ['(create new script)', '(skip)'],
614
- ).ask()
615
+ ).ask_async()
615
616
 
616
617
  if testgroup == '(create new script)':
617
- new_script_name = questionary.text(
618
+ new_script_name = await questionary.text(
618
619
  'Enter the name of the new .txt generatorScript file: '
619
- ).ask()
620
+ ).ask_async()
620
621
  new_script_path = pathlib.Path(new_script_name).with_suffix('.txt')
621
622
  new_script_path.parent.mkdir(parents=True, exist_ok=True)
622
623
  new_script_path.touch()
@@ -673,7 +674,8 @@ def stress(
673
674
  help='Compile an asset given its path.',
674
675
  )
675
676
  @package.within_problem
676
- def compile_command(
677
+ @syncer.sync
678
+ async def compile_command(
677
679
  path: Annotated[
678
680
  Optional[str],
679
681
  typer.Argument(help='Path to the asset to compile.'),
@@ -694,7 +696,7 @@ def compile_command(
694
696
  if path is None:
695
697
  import questionary
696
698
 
697
- path = questionary.path("What's the path to your asset?").ask()
699
+ path = await questionary.path("What's the path to your asset?").ask_async()
698
700
  if path is None:
699
701
  console.console.print('[error]No path specified.[/error]')
700
702
  raise typer.Exit(1)
@@ -10,6 +10,7 @@ from rbx.box import package
10
10
  from rbx.box.environment import get_extension_or_default
11
11
  from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
12
12
  from rbx.box.packaging.packager import BasePackager, BuiltStatement
13
+ from rbx.box.schema import TaskType
13
14
  from rbx.box.statements.schema import Statement
14
15
  from rbx.config import get_default_app_path, get_testlib
15
16
 
@@ -24,6 +25,10 @@ def test_time(time):
24
25
 
25
26
 
26
27
  class BocaPackager(BasePackager):
28
+ @classmethod
29
+ def task_types(cls) -> List[TaskType]:
30
+ return [TaskType.BATCH, TaskType.COMMUNICATION]
31
+
27
32
  def _get_main_statement(self) -> Statement:
28
33
  pkg = package.find_problem_package_or_die()
29
34
 
@@ -119,7 +124,7 @@ class BocaPackager(BasePackager):
119
124
  )
120
125
 
121
126
  def _get_compare(self) -> str:
122
- compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare'
127
+ compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare.sh'
123
128
  if not compare_path.exists():
124
129
  console.console.print(
125
130
  '[error]BOCA template compare script not found.[/error]'
@@ -145,7 +150,26 @@ class BocaPackager(BasePackager):
145
150
  .replace('{{checker_content}}', checker)
146
151
  )
147
152
 
153
+ def _get_interactor(self) -> str:
154
+ extension = get_extension_or_default('boca', BocaExtension)
155
+
156
+ interactor_path = (
157
+ get_default_app_path() / 'packagers' / 'boca' / 'interactor_compile.sh'
158
+ )
159
+ if not interactor_path.exists():
160
+ console.console.print(
161
+ '[error]BOCA template interactor compile script not found.[/error]'
162
+ )
163
+ raise typer.Exit(1)
164
+
165
+ interactor_text = interactor_path.read_text()
166
+ interactor = package.get_interactor().path.read_text()
167
+ return interactor_text.replace(
168
+ '{{rbxFlags}}', extension.flags_with_defaults()['cc']
169
+ ).replace('{{interactor_content}}', interactor)
170
+
148
171
  def _get_compile(self, language: BocaLanguage) -> str:
172
+ pkg = package.find_problem_package_or_die()
149
173
  extension = get_extension_or_default('boca', BocaExtension)
150
174
 
151
175
  compile_path = (
@@ -160,6 +184,10 @@ class BocaPackager(BasePackager):
160
184
  compile_text = compile_path.read_text()
161
185
 
162
186
  assert 'umask 0022' in compile_text
187
+ if pkg.type == TaskType.COMMUNICATION:
188
+ compile_text = compile_text.replace(
189
+ 'umask 0022', 'umask 0022\n\n' + self._get_interactor()
190
+ )
163
191
  compile_text = compile_text.replace(
164
192
  'umask 0022', 'umask 0022\n\n' + self._get_checker()
165
193
  )
@@ -197,7 +225,8 @@ class BocaPackager(BasePackager):
197
225
  )
198
226
  )
199
227
 
200
- def name(self) -> str:
228
+ @classmethod
229
+ def name(cls) -> str:
201
230
  return 'boca'
202
231
 
203
232
  def package(
@@ -207,7 +236,7 @@ class BocaPackager(BasePackager):
207
236
  built_statements: List[BuiltStatement],
208
237
  ) -> pathlib.Path:
209
238
  extension = get_extension_or_default('boca', BocaExtension)
210
-
239
+ pkg = package.find_problem_package_or_die()
211
240
  # Prepare limits
212
241
  limits_path = into_path / 'limits'
213
242
  limits_path.mkdir(parents=True, exist_ok=True)
@@ -227,9 +256,17 @@ class BocaPackager(BasePackager):
227
256
  run_orig_path = (
228
257
  get_default_app_path() / 'packagers' / 'boca' / 'run' / language
229
258
  )
259
+ if pkg.type == TaskType.COMMUNICATION:
260
+ run_orig_path = (
261
+ get_default_app_path()
262
+ / 'packagers'
263
+ / 'boca'
264
+ / 'interactive'
265
+ / language
266
+ )
230
267
  if not run_orig_path.is_file():
231
268
  console.console.print(
232
- f'[error]Run script for language [item]{language}[/item] not found.[/error]'
269
+ f'[error]Run script for language [item]{language}[/item] not found for task of type [item]{pkg.type}[/item].[/error]'
233
270
  )
234
271
  raise typer.Exit(1)
235
272
  shutil.copyfile(run_orig_path, run_path / language)
@@ -268,11 +305,11 @@ class BocaPackager(BasePackager):
268
305
 
269
306
  testcases = self.get_flattened_built_testcases()
270
307
  for i, testcase in enumerate(testcases):
271
- shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
308
+ shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
272
309
  if testcase.outputPath is not None:
273
- shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
310
+ shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
274
311
  else:
275
- (outputs_path / f'{i+1:03d}').touch()
312
+ (outputs_path / f'{i + 1:03d}').touch()
276
313
 
277
314
  # Zip all.
278
315
  shutil.make_archive(
@@ -28,6 +28,13 @@ async def run_packager(
28
28
  raise typer.Exit(1)
29
29
 
30
30
  pkg = package.find_problem_package_or_die()
31
+
32
+ if pkg.type not in packager_cls.task_types():
33
+ console.console.print(
34
+ f'[error]Packager [item]{packager_cls.name()}[/item] does not support task type [item]{pkg.type}[/item].[/error]'
35
+ )
36
+ raise typer.Exit(1)
37
+
31
38
  packager = packager_cls(**kwargs)
32
39
 
33
40
  statement_types = packager.statement_types()
@@ -7,10 +7,10 @@ import typer
7
7
  from rbx import console
8
8
  from rbx.box import package
9
9
  from rbx.box.environment import get_extension_or_default
10
- from rbx.box.packaging.boca.extension import BocaExtension
10
+ from rbx.box.packaging.boca.extension import BocaExtension, BocaLanguage
11
11
  from rbx.box.packaging.boca.packager import BocaPackager
12
12
  from rbx.box.packaging.packager import BuiltStatement
13
- from rbx.box.schema import ExpectedOutcome
13
+ from rbx.box.schema import ExpectedOutcome, TaskType
14
14
  from rbx.config import get_default_app_path, get_testlib
15
15
  from rbx.grading.judge.digester import digest_cooperatively
16
16
 
@@ -20,6 +20,10 @@ class MojPackager(BocaPackager):
20
20
  super().__init__()
21
21
  self.for_boca = for_boca
22
22
 
23
+ @classmethod
24
+ def task_types(cls) -> List[TaskType]:
25
+ return [TaskType.COMMUNICATION, TaskType.BATCH]
26
+
23
27
  def _get_problem_info(self) -> str:
24
28
  statement = self._get_main_statement()
25
29
  return (
@@ -40,8 +44,12 @@ class MojPackager(BocaPackager):
40
44
  def _get_limits(self) -> str:
41
45
  pkg = package.find_problem_package_or_die()
42
46
  ml = pkg.memoryLimit
43
- ol = pkg.outputLimit
44
- return f'ULIMITS[-f]={ol}\n' f'ULIMITS[-v]={ml * 1024}\n'
47
+ # ol = pkg.outputLimit
48
+ limits = [
49
+ f'ULIMITS[-v]={ml * 1024}',
50
+ # f'ULIMITS[-f]={ol}',
51
+ ]
52
+ return '\n'.join(limits) + '\n'
45
53
 
46
54
  def _get_compare(self) -> str:
47
55
  extension = get_extension_or_default('boca', BocaExtension)
@@ -65,6 +73,33 @@ class MojPackager(BocaPackager):
65
73
  def _get_checker(self) -> str:
66
74
  return package.get_checker().path.read_text()
67
75
 
76
+ def _get_interactor(self) -> str:
77
+ return package.get_interactor().path.read_text()
78
+
79
+ def _expand_language_vars(self, language: BocaLanguage, dir: pathlib.Path):
80
+ extension = get_extension_or_default('boca', BocaExtension)
81
+
82
+ for path in dir.glob('**/*'):
83
+ if not path.is_file():
84
+ continue
85
+
86
+ replaced = path.read_text()
87
+ replaced = replaced.replace(
88
+ '{{rbxMaxMemory}}', f'{self._get_pkg_memorylimit(language)}'
89
+ ).replace(
90
+ '{{rbxInitialMemory}}',
91
+ f'{min(512, int(self._get_pkg_memorylimit(language) * 0.9))}',
92
+ )
93
+
94
+ flags = extension.flags_with_defaults()
95
+ if language in flags:
96
+ replaced = replaced.replace('{{rbxFlags}}', flags[language])
97
+
98
+ path.write_text(replaced)
99
+
100
+ if path.suffix == '.sh':
101
+ path.chmod(0o755)
102
+
68
103
  def _copy_solutions_moj(self, into_path: pathlib.Path):
69
104
  into_path = into_path / 'sols'
70
105
  has_good = False
@@ -83,7 +118,8 @@ class MojPackager(BocaPackager):
83
118
  console.console.print('[error]No good solution found.[/error]')
84
119
  raise typer.Exit(1)
85
120
 
86
- def name(self) -> str:
121
+ @classmethod
122
+ def name(cls) -> str:
87
123
  return 'moj'
88
124
 
89
125
  def package(
@@ -92,6 +128,8 @@ class MojPackager(BocaPackager):
92
128
  into_path: pathlib.Path,
93
129
  built_statements: List[BuiltStatement],
94
130
  ) -> pathlib.Path:
131
+ pkg = package.find_problem_package_or_die()
132
+
95
133
  # Prepare dummy files
96
134
  author_path = into_path / 'author'
97
135
  author_path.parent.mkdir(parents=True, exist_ok=True)
@@ -128,6 +166,48 @@ class MojPackager(BocaPackager):
128
166
  checker_path.parent.mkdir(parents=True, exist_ok=True)
129
167
  checker_path.write_text(self._get_checker())
130
168
 
169
+ # Prepare interactor
170
+ if pkg.type == TaskType.COMMUNICATION:
171
+ interactor_path = into_path / 'scripts' / 'interactor.cpp'
172
+ interactor_path.parent.mkdir(parents=True, exist_ok=True)
173
+ interactor_path.write_text(self._get_interactor())
174
+
175
+ interactor_prep_path = into_path / 'scripts' / 'interactor_prep.sh'
176
+ interactor_prep_path.parent.mkdir(parents=True, exist_ok=True)
177
+ shutil.copy(
178
+ get_default_app_path()
179
+ / 'packagers'
180
+ / 'moj'
181
+ / 'scripts'
182
+ / 'interactor_prep.sh',
183
+ interactor_prep_path,
184
+ )
185
+ interactor_prep_path.chmod(0o755)
186
+
187
+ interactor_run_path = into_path / 'scripts' / 'interactor_run.sh'
188
+ interactor_run_path.parent.mkdir(parents=True, exist_ok=True)
189
+ shutil.copy(
190
+ get_default_app_path()
191
+ / 'packagers'
192
+ / 'moj'
193
+ / 'scripts'
194
+ / 'interactor_run.sh',
195
+ interactor_run_path,
196
+ )
197
+ interactor_run_path.chmod(0o755)
198
+
199
+ # Prepare language scripts
200
+ extension = get_extension_or_default('boca', BocaExtension)
201
+ for language in extension.languages:
202
+ language_path = into_path / 'scripts' / language
203
+ language_path.parent.mkdir(parents=True, exist_ok=True)
204
+ src_path = (
205
+ get_default_app_path() / 'packagers' / 'moj' / 'scripts' / language
206
+ )
207
+ if src_path.exists():
208
+ shutil.copytree(src_path, language_path)
209
+ self._expand_language_vars(language, language_path)
210
+
131
211
  # Problem statement
132
212
  enunciado_path = into_path / 'docs' / 'enunciado.pdf'
133
213
  enunciado_path.parent.mkdir(parents=True, exist_ok=True)
@@ -150,11 +230,11 @@ class MojPackager(BocaPackager):
150
230
 
151
231
  testcases = self.get_flattened_built_testcases()
152
232
  for i, testcase in enumerate(testcases):
153
- shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
233
+ shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
154
234
  if testcase.outputPath is not None:
155
- shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
235
+ shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
156
236
  else:
157
- (outputs_path / f'{i+1:03d}').touch()
237
+ (outputs_path / f'{i + 1:03d}').touch()
158
238
 
159
239
  # Zip all.
160
240
  shutil.make_archive(
@@ -7,7 +7,7 @@ from rbx.box import package
7
7
  from rbx.box.contest import contest_package
8
8
  from rbx.box.contest.schema import ContestProblem, ContestStatement
9
9
  from rbx.box.generators import get_all_built_testcases
10
- from rbx.box.schema import Package, Testcase, TestcaseGroup
10
+ from rbx.box.schema import Package, TaskType, Testcase, TestcaseGroup
11
11
  from rbx.box.statements.schema import Statement, StatementType
12
12
 
13
13
 
@@ -33,10 +33,15 @@ class BuiltProblemPackage:
33
33
 
34
34
 
35
35
  class BasePackager(ABC):
36
+ @classmethod
36
37
  @abstractmethod
37
- def name(self) -> str:
38
+ def name(cls) -> str:
38
39
  pass
39
40
 
41
+ @classmethod
42
+ def task_types(cls) -> List[TaskType]:
43
+ return [TaskType.BATCH]
44
+
40
45
  def languages(self):
41
46
  pkg = package.find_problem_package_or_die()
42
47
 
@@ -188,15 +188,15 @@ class PolygonPackager(BasePackager):
188
188
  for i, testcase in enumerate(testcases):
189
189
  shutil.copyfile(
190
190
  testcase.inputPath,
191
- into_path / f'tests/{i+1:03d}',
191
+ into_path / f'tests/{i + 1:03d}',
192
192
  )
193
193
  if testcase.outputPath is not None:
194
194
  shutil.copyfile(
195
195
  testcase.outputPath,
196
- into_path / f'tests/{i+1:03d}.a',
196
+ into_path / f'tests/{i + 1:03d}.a',
197
197
  )
198
198
  else:
199
- (into_path / f'tests/{i+1:03d}.a').touch()
199
+ (into_path / f'tests/{i + 1:03d}.a').touch()
200
200
 
201
201
  # Write problem.xml
202
202
  (into_path / 'problem.xml').write_text(descriptor)
@@ -208,7 +208,8 @@ class PolygonPackager(BasePackager):
208
208
 
209
209
 
210
210
  class PolygonContestPackager(BaseContestPackager):
211
- def name(self) -> str:
211
+ @classmethod
212
+ def name(cls) -> str:
212
213
  return 'polygon'
213
214
 
214
215
  def _get_names(self) -> List[polygon_schema.Name]:
@@ -616,7 +616,7 @@ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
616
616
  ]
617
617
 
618
618
 
619
- def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
619
+ async def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
620
620
  pkg = package.find_problem_package_or_die()
621
621
  if tracked_solutions is None:
622
622
  tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
@@ -630,7 +630,7 @@ def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
630
630
  if str(sol.path) in tracked_solutions
631
631
  ]
632
632
 
633
- picked = questionary.checkbox('Select solutions', choices=choices).ask()
633
+ picked = await questionary.checkbox('Select solutions', choices=choices).ask_async()
634
634
  if picked is None:
635
635
  raise typer.Abort()
636
636
  return picked
@@ -50,7 +50,6 @@ def _compile_finder(finder: CodeItem) -> str:
50
50
  return digest
51
51
 
52
52
 
53
- @syncer.sync
54
53
  async def run_stress(
55
54
  name: str,
56
55
  timeoutInSeconds: int,
@@ -194,7 +194,8 @@ async def _run_communication_solution_on_testcase(
194
194
  output_path = testcase.outputPath
195
195
  else:
196
196
  output_path = output_dir / testcase.inputPath.with_suffix('.out').name
197
- error_path = output_path.with_suffix('.err')
197
+ solution_error_path = output_path.with_suffix('.sol.err')
198
+ interactor_error_path = output_path.with_suffix('.int.err')
198
199
  log_path = output_path.with_suffix('.log')
199
200
  output_path.parent.mkdir(parents=True, exist_ok=True)
200
201
 
@@ -204,7 +205,7 @@ async def _run_communication_solution_on_testcase(
204
205
  interactor_item = CommunicationItem(
205
206
  code=package.get_interactor(),
206
207
  executable=DigestOrSource.create(interactor_digest),
207
- stderr=DigestOrDest.create(error_path),
208
+ stderr=DigestOrDest.create(interactor_error_path),
208
209
  extra_config=interactor_extra_config,
209
210
  extra_args='interactor.in interactor.out',
210
211
  inputs=[
@@ -230,6 +231,7 @@ async def _run_communication_solution_on_testcase(
230
231
  solution_item = CommunicationItem(
231
232
  code=solution,
232
233
  executable=DigestOrSource.create(compiled_digest),
234
+ stderr=DigestOrDest.create(solution_error_path),
233
235
  extra_config=extra_config,
234
236
  capture=DigestOrDest.create(solution_capture_path)
235
237
  if solution_capture_path
@@ -248,7 +250,7 @@ async def _run_communication_solution_on_testcase(
248
250
  checker_digest,
249
251
  run_log,
250
252
  interactor_run_log,
251
- error_path,
253
+ interactor_error_path,
252
254
  testcase,
253
255
  output_path,
254
256
  )
@@ -263,7 +265,7 @@ async def _run_communication_solution_on_testcase(
263
265
  log=TestcaseLog(
264
266
  **(run_log.model_dump() if run_log is not None else {}),
265
267
  stdout_absolute_path=output_path.absolute(),
266
- stderr_absolute_path=error_path.absolute(),
268
+ stderr_absolute_path=solution_error_path.absolute(),
267
269
  log_absolute_path=log_path.absolute(),
268
270
  ),
269
271
  )