rbx.cp 0.5.45__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.45 → rbx_cp-0.5.47}/PKG-INFO +1 -1
  2. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/pyproject.toml +1 -1
  3. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/checkers.py +81 -28
  4. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/cli.py +12 -10
  5. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/generators.py +77 -40
  6. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/boca/packager.py +44 -7
  7. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/main.py +7 -0
  8. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/moj/packager.py +88 -8
  9. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/packager.py +7 -2
  10. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/packager.py +5 -4
  11. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/solutions.py +7 -5
  12. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/builders.py +22 -3
  13. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/stresses.py +0 -1
  14. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/tasks.py +18 -8
  15. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/testcase_utils.py +66 -0
  16. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/sandbox.py +29 -1
  17. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/isolate.py +12 -2
  18. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/stupid_sandbox.py +17 -5
  19. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/timeit.py +12 -3
  20. rbx_cp-0.5.47/rbx/grading/processing_context.py +48 -0
  21. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/steps.py +24 -13
  22. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/checker.sh +8 -6
  23. rbx_cp-0.5.47/rbx/resources/packagers/boca/compare.sh +48 -0
  24. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/c +207 -0
  25. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/cc +207 -0
  26. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/cpp +207 -0
  27. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/java +240 -0
  28. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/kt +231 -0
  29. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/py2 +209 -0
  30. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactive/py3 +209 -0
  31. rbx_cp-0.5.47/rbx/resources/packagers/boca/interactor_compile.sh +45 -0
  32. rbx_cp-0.5.47/rbx/resources/packagers/boca/run/bkp +163 -0
  33. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/c +19 -19
  34. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/cc +19 -19
  35. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/cpp +19 -19
  36. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/java +51 -51
  37. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/kt +30 -30
  38. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/py2 +42 -42
  39. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/run/py3 +42 -42
  40. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/compile.sh +19 -0
  41. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/prep.sh +5 -0
  42. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/c/run.sh +16 -0
  43. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/moj/scripts/compare.sh +32 -6
  44. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/compile.sh +19 -0
  45. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/prep.sh +5 -0
  46. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/cpp/run.sh +16 -0
  47. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/interactor_prep.sh +14 -0
  48. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/interactor_run.sh +38 -0
  49. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/compile.sh +12 -0
  50. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/prep.sh +8 -0
  51. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/java/run.sh +17 -0
  52. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/compile.sh +7 -0
  53. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/prep.sh +5 -0
  54. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py2/run.sh +16 -0
  55. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/compile.sh +7 -0
  56. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/prep.sh +5 -0
  57. rbx_cp-0.5.47/rbx/resources/packagers/moj/scripts/py3/run.sh +16 -0
  58. rbx_cp-0.5.45/rbx/resources/packagers/boca/compare +0 -53
  59. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/LICENSE +0 -0
  60. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/README.md +0 -0
  61. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/__init__.py +0 -0
  62. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/annotations.py +0 -0
  63. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/autoenum.py +0 -0
  64. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/__init__.py +0 -0
  65. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/builder.py +0 -0
  66. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/cd.py +0 -0
  67. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/code.py +0 -0
  68. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/compile.py +0 -0
  69. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/conftest.py +0 -0
  70. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/__init__.py +0 -0
  71. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/build_contest_statements.py +0 -0
  72. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/contest_package.py +0 -0
  73. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/contest_utils.py +0 -0
  74. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/main.py +0 -0
  75. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/schema.py +0 -0
  76. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/contest/statements.py +0 -0
  77. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/creation.py +0 -0
  78. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/deferred.py +0 -0
  79. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/download.py +0 -0
  80. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/environment.py +0 -0
  81. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/extensions.py +0 -0
  82. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/formatting.py +0 -0
  83. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/generators_test.py +0 -0
  84. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/lazy_importing_main.py +0 -0
  85. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/lazy_importing_test.py +0 -0
  86. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/main.py +0 -0
  87. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/package.py +0 -0
  88. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/boca/extension.py +0 -0
  89. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/contest_main.py +0 -0
  90. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/test.py +0 -0
  91. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  92. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/presets/__init__.py +0 -0
  93. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/presets/fetch.py +0 -0
  94. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/presets/lock_schema.py +0 -0
  95. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/presets/schema.py +0 -0
  96. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/retries.py +0 -0
  97. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/sanitizers/warning_stack.py +0 -0
  98. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/schema.py +0 -0
  99. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/setter_config.py +0 -0
  100. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/solutions_test.py +0 -0
  101. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/state.py +0 -0
  102. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/__init__.py +0 -0
  103. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/build_statements.py +0 -0
  104. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/joiners.py +0 -0
  105. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/latex.py +0 -0
  106. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/latex_jinja.py +0 -0
  107. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/statements/schema.py +0 -0
  108. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/stressing/__init__.py +0 -0
  109. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/stressing/finder_parser.py +0 -0
  110. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/stressing/generator_parser.py +0 -0
  111. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/testcase_extractors.py +0 -0
  112. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/testcases/__init__.py +0 -0
  113. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/testcases/main.py +0 -0
  114. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/ui/__init__.py +0 -0
  115. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/ui/captured_log.py +0 -0
  116. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/ui/css/app.tcss +0 -0
  117. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/ui/main.py +0 -0
  118. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/ui/run.py +0 -0
  119. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/unit.py +0 -0
  120. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/validators.py +0 -0
  121. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/box/validators_test.py +0 -0
  122. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/checker.py +0 -0
  123. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/clone.py +0 -0
  124. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/config.py +0 -0
  125. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/conftest.py +0 -0
  126. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/console.py +0 -0
  127. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/create.py +0 -0
  128. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/edit.py +0 -0
  129. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/__init__.py +0 -0
  130. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/caching.py +0 -0
  131. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/conftest.py +0 -0
  132. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/__init__.py +0 -0
  133. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/cacher.py +0 -0
  134. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/digester.py +0 -0
  135. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  136. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/storage.py +0 -0
  137. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/test.py +0 -0
  138. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/judge/testiso.py +0 -0
  139. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/steps_with_caching.py +0 -0
  140. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading/steps_with_caching_run_test.py +0 -0
  141. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/grading_utils.py +0 -0
  142. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/hydration.py +0 -0
  143. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/main.py +0 -0
  144. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/metadata.py +0 -0
  145. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/providers/__init__.py +0 -0
  146. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/providers/codeforces.py +0 -0
  147. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/providers/provider.py +0 -0
  148. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/checkers/boilerplate.cpp +0 -0
  149. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/default_config.json +0 -0
  150. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/default_setter_config.mac.yml +0 -0
  151. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/default_setter_config.yml +0 -0
  152. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/envs/default.rbx.yml +0 -0
  153. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/envs/isolate.rbx.yml +0 -0
  154. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/c +0 -0
  155. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/cc +0 -0
  156. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/cpp +0 -0
  157. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/java +0 -0
  158. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/kt +0 -0
  159. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/pas +0 -0
  160. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/py2 +0 -0
  161. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/packagers/boca/compile/py3 +0 -0
  162. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  163. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  164. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  165. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  166. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  167. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/.gitignore +0 -0
  168. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  169. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  170. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/random.py +0 -0
  171. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/random.txt +0 -0
  172. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  173. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  174. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  175. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  176. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  177. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  178. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  179. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  180. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  181. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  182. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  183. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/resources/templates/template.cpp +0 -0
  184. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/run.py +0 -0
  185. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/schema.py +0 -0
  186. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/submit.py +0 -0
  187. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/submitors/__init__.py +0 -0
  188. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/submitors/codeforces.py +0 -0
  189. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/submitors/submitor.py +0 -0
  190. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/test.py +0 -0
  191. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/testcase.py +0 -0
  192. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/testcase_rendering.py +0 -0
  193. {rbx_cp-0.5.45 → rbx_cp-0.5.47}/rbx/testing_utils.py +0 -0
  194. {rbx_cp-0.5.45 → 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.45
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.45"
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)
@@ -1,6 +1,5 @@
1
1
  import pathlib
2
2
  import shutil
3
- import tempfile
4
3
  from typing import Dict, List, Optional, Set
5
4
 
6
5
  import typer
@@ -63,6 +62,47 @@ def _copy_testcase_over(
63
62
  )
64
63
 
65
64
 
65
+ def _copy_testcase_output_over(
66
+ src_output_path: pathlib.Path, dest_output_path: pathlib.Path, suffix: str
67
+ ) -> bool:
68
+ dest_output_path.parent.mkdir(parents=True, exist_ok=True)
69
+
70
+ src_path = src_output_path.with_suffix(suffix)
71
+ if not src_path.is_file():
72
+ return False
73
+
74
+ shutil.copy(str(src_path), str(dest_output_path.with_suffix(suffix)))
75
+ return True
76
+
77
+
78
+ def _copy_testcase_outputs_over(
79
+ testcase: Testcase, dest: Testcase, pipes: bool = False
80
+ ):
81
+ assert dest.outputPath is not None
82
+ dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
83
+
84
+ has_copied = False
85
+
86
+ if testcase.outputPath is not None and testcase.outputPath.is_file():
87
+ shutil.copy(str(testcase.outputPath), str(dest.outputPath))
88
+ has_copied = True
89
+
90
+ if not pipes:
91
+ return has_copied
92
+
93
+ reference_path = testcase.outputPath or testcase.inputPath
94
+ if _copy_testcase_output_over(reference_path, dest.outputPath, '.pin'):
95
+ has_copied = True
96
+
97
+ if _copy_testcase_output_over(reference_path, dest.outputPath, '.pout'):
98
+ has_copied = True
99
+
100
+ if _copy_testcase_output_over(reference_path, dest.outputPath, '.pio'):
101
+ has_copied = True
102
+
103
+ return has_copied
104
+
105
+
66
106
  def get_all_built_testcases() -> Dict[str, List[Testcase]]:
67
107
  pkg = package.find_problem_package_or_die()
68
108
  res = {group.name: find_built_testcases(group) for group in pkg.testcases}
@@ -256,59 +296,46 @@ async def generate_testcases(
256
296
  async def generate_output_for_testcase(
257
297
  main_solution_digest: str,
258
298
  testcase: Testcase,
259
- stderr_path: Optional[pathlib.Path] = None,
260
299
  interactor_digest: Optional[str] = None,
261
300
  ):
262
301
  assert testcase.outputPath is not None
263
302
  testcase.inputPath.parent.mkdir(parents=True, exist_ok=True)
264
303
  testcase.outputPath.parent.mkdir(parents=True, exist_ok=True)
265
304
 
266
- if testcase.outputPath.is_file():
267
- # Output file was already copied over from manual tests.
268
- return
269
-
270
305
  main_solution = package.get_main_solution()
271
306
  if main_solution is None:
272
307
  return
273
308
 
274
- with tempfile.TemporaryDirectory() as dir:
275
- output_dir = pathlib.Path(dir)
309
+ eval: Evaluation = await run_solution_on_testcase(
310
+ main_solution,
311
+ main_solution_digest,
312
+ None,
313
+ testcase,
314
+ interactor_digest=interactor_digest,
315
+ use_retries=False,
316
+ use_timelimit=False,
317
+ capture_pipes=True,
318
+ )
276
319
 
277
- eval: Evaluation = await run_solution_on_testcase(
278
- main_solution,
279
- main_solution_digest,
280
- None,
281
- testcase,
282
- output_dir,
283
- interactor_digest=interactor_digest,
284
- use_retries=False,
285
- use_timelimit=False,
320
+ if eval.result.outcome != Outcome.ACCEPTED:
321
+ console.console.print(
322
+ f'[error]Failed generating output for [item]{testcase.inputPath}[/item][/error]',
286
323
  )
287
-
288
- if eval.log.stdout_absolute_path is not None:
289
- shutil.copy(eval.log.stdout_absolute_path, testcase.outputPath)
290
- if eval.log.stderr_absolute_path is not None and stderr_path is not None:
291
- shutil.copy(eval.log.stderr_absolute_path, stderr_path)
292
-
293
- if eval.result.outcome != Outcome.ACCEPTED:
294
- console.console.print(
295
- f'[error]Failed generating output for [item]{testcase.inputPath}[/item][/error]',
296
- )
297
- console.console.print(f'[error]Summary:[/error] {eval.log.get_summary()}')
298
- console.console.print(
299
- f'[warning]Verdict: [item]{eval.result.outcome.value}[/item][/warning]',
300
- )
301
- console.console.print(
302
- f'[warning]Message: [info]{eval.result.message}[/info][/warning]',
303
- )
304
- console.console.print(f'Input written at [item]{testcase.inputPath}[/item]')
324
+ console.console.print(f'[error]Summary:[/error] {eval.log.get_summary()}')
325
+ console.console.print(
326
+ f'[warning]Verdict: [item]{eval.result.outcome.value}[/item][/warning]',
327
+ )
328
+ console.console.print(
329
+ f'[warning]Message: [info]{eval.result.message}[/info][/warning]',
330
+ )
331
+ console.console.print(f'Input written at [item]{testcase.inputPath}[/item]')
332
+ console.console.print(f'Output written at [item]{testcase.outputPath}[/item]')
333
+ if eval.log.stderr_absolute_path is not None:
305
334
  console.console.print(
306
- f'Output written at [item]{testcase.outputPath}[/item]'
335
+ f'Stderr written at [item]{eval.log.stderr_absolute_path}[/item]'
307
336
  )
308
- if stderr_path is not None:
309
- console.console.print(f'Stderr written at [item]{stderr_path}[/item]')
310
337
 
311
- raise typer.Exit(1)
338
+ raise typer.Exit(1)
312
339
 
313
340
 
314
341
  async def generate_outputs_for_testcases(
@@ -350,6 +377,14 @@ async def generate_outputs_for_testcases(
350
377
  return
351
378
  assert tc.outputPath is not None
352
379
 
380
+ if entry.metadata.copied_from is not None and _copy_testcase_outputs_over(
381
+ entry.metadata.copied_from, tc
382
+ ):
383
+ # Copy remaining pipe files.
384
+ _copy_testcase_outputs_over(entry.metadata.copied_from, tc, pipes=True)
385
+ step()
386
+ continue
387
+
353
388
  if (
354
389
  main_solution is None or solution_digest is None
355
390
  ) and not tc.outputPath.is_file():
@@ -362,7 +397,9 @@ async def generate_outputs_for_testcases(
362
397
  await generate_output_for_testcase(
363
398
  solution_digest,
364
399
  tc,
365
- gen_runs_dir / 'main.stderr',
366
400
  interactor_digest=interactor_digest,
367
401
  )
402
+ if entry.metadata.copied_from is not None:
403
+ # Copy remaining pipe files.
404
+ _copy_testcase_outputs_over(entry.metadata.copied_from, tc, pipes=True)
368
405
  step()
@@ -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()