rbx.cp 0.5.46__tar.gz → 0.5.48__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 (195) hide show
  1. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/PKG-INFO +1 -1
  2. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/pyproject.toml +1 -1
  3. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/checkers.py +81 -28
  4. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/cli.py +12 -10
  5. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/environment.py +1 -1
  6. rbx_cp-0.5.48/rbx/box/naming.py +22 -0
  7. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/boca/extension.py +1 -0
  8. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/boca/packager.py +54 -11
  9. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/main.py +7 -0
  10. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/moj/packager.py +89 -19
  11. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/packager.py +15 -3
  12. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/polygon/packager.py +5 -4
  13. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/solutions.py +2 -2
  14. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/build_statements.py +2 -1
  15. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/stresses.py +0 -1
  16. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/tasks.py +6 -4
  17. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/sandbox.py +29 -1
  18. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/sandboxes/isolate.py +10 -0
  19. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/sandboxes/stupid_sandbox.py +16 -4
  20. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/sandboxes/timeit.py +12 -3
  21. rbx_cp-0.5.48/rbx/grading/processing_context.py +48 -0
  22. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/steps.py +25 -14
  23. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/checker.sh +8 -6
  24. rbx_cp-0.5.48/rbx/resources/packagers/boca/compare.sh +48 -0
  25. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/c +207 -0
  26. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/cc +207 -0
  27. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/cpp +207 -0
  28. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/java +240 -0
  29. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/kt +231 -0
  30. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/py2 +209 -0
  31. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactive/py3 +209 -0
  32. rbx_cp-0.5.48/rbx/resources/packagers/boca/interactor_compile.sh +45 -0
  33. rbx_cp-0.5.48/rbx/resources/packagers/boca/run/bkp +163 -0
  34. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/c +19 -19
  35. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/cc +19 -19
  36. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/cpp +19 -19
  37. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/java +51 -51
  38. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/kt +30 -30
  39. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/py2 +42 -42
  40. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/run/py3 +42 -42
  41. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/c/compile.sh +19 -0
  42. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/c/prep.sh +5 -0
  43. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/c/run.sh +16 -0
  44. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/moj/scripts/compare.sh +32 -6
  45. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/cpp/compile.sh +19 -0
  46. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/cpp/prep.sh +5 -0
  47. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/cpp/run.sh +16 -0
  48. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/interactor_prep.sh +14 -0
  49. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/interactor_run.sh +38 -0
  50. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/java/compile.sh +12 -0
  51. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/java/prep.sh +8 -0
  52. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/java/run.sh +17 -0
  53. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py2/compile.sh +7 -0
  54. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py2/prep.sh +5 -0
  55. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py2/run.sh +16 -0
  56. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py3/compile.sh +7 -0
  57. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py3/prep.sh +5 -0
  58. rbx_cp-0.5.48/rbx/resources/packagers/moj/scripts/py3/run.sh +16 -0
  59. rbx_cp-0.5.46/rbx/resources/packagers/boca/compare +0 -53
  60. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/LICENSE +0 -0
  61. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/README.md +0 -0
  62. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/__init__.py +0 -0
  63. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/annotations.py +0 -0
  64. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/autoenum.py +0 -0
  65. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/__init__.py +0 -0
  66. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/builder.py +0 -0
  67. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/cd.py +0 -0
  68. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/code.py +0 -0
  69. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/compile.py +0 -0
  70. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/conftest.py +0 -0
  71. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/__init__.py +0 -0
  72. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/build_contest_statements.py +0 -0
  73. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/contest_package.py +0 -0
  74. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/contest_utils.py +0 -0
  75. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/main.py +0 -0
  76. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/schema.py +0 -0
  77. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/contest/statements.py +0 -0
  78. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/creation.py +0 -0
  79. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/deferred.py +0 -0
  80. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/download.py +0 -0
  81. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/extensions.py +0 -0
  82. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/formatting.py +0 -0
  83. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/generators.py +0 -0
  84. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/generators_test.py +0 -0
  85. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/lazy_importing_main.py +0 -0
  86. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/lazy_importing_test.py +0 -0
  87. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/main.py +0 -0
  88. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/package.py +0 -0
  89. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/contest_main.py +0 -0
  90. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/polygon/test.py +0 -0
  91. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  92. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/presets/__init__.py +0 -0
  93. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/presets/fetch.py +0 -0
  94. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/presets/lock_schema.py +0 -0
  95. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/presets/schema.py +0 -0
  96. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/retries.py +0 -0
  97. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/sanitizers/warning_stack.py +0 -0
  98. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/schema.py +0 -0
  99. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/setter_config.py +0 -0
  100. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/solutions_test.py +0 -0
  101. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/state.py +0 -0
  102. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/__init__.py +0 -0
  103. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/builders.py +0 -0
  104. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/joiners.py +0 -0
  105. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/latex.py +0 -0
  106. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/latex_jinja.py +0 -0
  107. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/statements/schema.py +0 -0
  108. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/stressing/__init__.py +0 -0
  109. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/stressing/finder_parser.py +0 -0
  110. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/stressing/generator_parser.py +0 -0
  111. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/testcase_extractors.py +0 -0
  112. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/testcase_utils.py +0 -0
  113. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/testcases/__init__.py +0 -0
  114. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/testcases/main.py +0 -0
  115. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/ui/__init__.py +0 -0
  116. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/ui/captured_log.py +0 -0
  117. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/ui/css/app.tcss +0 -0
  118. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/ui/main.py +0 -0
  119. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/ui/run.py +0 -0
  120. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/unit.py +0 -0
  121. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/validators.py +0 -0
  122. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/box/validators_test.py +0 -0
  123. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/checker.py +0 -0
  124. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/clone.py +0 -0
  125. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/config.py +0 -0
  126. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/conftest.py +0 -0
  127. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/console.py +0 -0
  128. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/create.py +0 -0
  129. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/edit.py +0 -0
  130. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/__init__.py +0 -0
  131. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/caching.py +0 -0
  132. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/conftest.py +0 -0
  133. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/__init__.py +0 -0
  134. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/cacher.py +0 -0
  135. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/digester.py +0 -0
  136. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  137. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/storage.py +0 -0
  138. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/test.py +0 -0
  139. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/judge/testiso.py +0 -0
  140. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/steps_with_caching.py +0 -0
  141. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading/steps_with_caching_run_test.py +0 -0
  142. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/grading_utils.py +0 -0
  143. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/hydration.py +0 -0
  144. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/main.py +0 -0
  145. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/metadata.py +0 -0
  146. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/providers/__init__.py +0 -0
  147. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/providers/codeforces.py +0 -0
  148. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/providers/provider.py +0 -0
  149. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/checkers/boilerplate.cpp +0 -0
  150. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/default_config.json +0 -0
  151. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/default_setter_config.mac.yml +0 -0
  152. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/default_setter_config.yml +0 -0
  153. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/envs/default.rbx.yml +0 -0
  154. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/envs/isolate.rbx.yml +0 -0
  155. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/c +0 -0
  156. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/cc +0 -0
  157. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/cpp +0 -0
  158. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/java +0 -0
  159. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/kt +0 -0
  160. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/pas +0 -0
  161. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/py2 +0 -0
  162. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/packagers/boca/compile/py3 +0 -0
  163. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  164. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  165. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  166. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  167. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  168. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/.gitignore +0 -0
  169. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  170. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  171. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/random.py +0 -0
  172. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/random.txt +0 -0
  173. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  174. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  175. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  176. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  177. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  178. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  179. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  180. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  181. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  182. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  183. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  184. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/resources/templates/template.cpp +0 -0
  185. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/run.py +0 -0
  186. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/schema.py +0 -0
  187. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/submit.py +0 -0
  188. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/submitors/__init__.py +0 -0
  189. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/submitors/codeforces.py +0 -0
  190. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/submitors/submitor.py +0 -0
  191. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/test.py +0 -0
  192. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/testcase.py +0 -0
  193. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/testcase_rendering.py +0 -0
  194. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/rbx/testing_utils.py +0 -0
  195. {rbx_cp-0.5.46 → rbx_cp-0.5.48}/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.48
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.48"
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)
@@ -343,7 +343,7 @@ def get_extension(name: str, _: Type[T]) -> Optional[T]:
343
343
  pkg = get_environment()
344
344
  if pkg.extensions is None:
345
345
  return None
346
- if hasattr(pkg.extensions, name):
346
+ if not hasattr(pkg.extensions, name):
347
347
  return None
348
348
  return getattr(pkg.extensions, name)
349
349
 
@@ -0,0 +1,22 @@
1
+ from typing import Optional
2
+
3
+ from rbx.box import package
4
+ from rbx.box.contest import contest_package
5
+
6
+
7
+ def get_problem_shortname() -> Optional[str]:
8
+ contest = contest_package.find_contest_package()
9
+ if contest is None:
10
+ return None
11
+ problem_path = package.find_problem()
12
+ contest_path = contest_package.find_contest()
13
+
14
+ for problem in contest.problems:
15
+ if problem.path is None:
16
+ continue
17
+ if (problem_path / 'problem.rbx.yml').samefile(
18
+ contest_path / problem.path / 'problem.rbx.yml'
19
+ ):
20
+ return problem.short_name
21
+
22
+ return None
@@ -11,6 +11,7 @@ class BocaExtension(BaseModel):
11
11
  languages: typing.List[BocaLanguage] = list(typing.get_args(BocaLanguage))
12
12
  flags: typing.Dict[BocaLanguage, str] = {}
13
13
  maximumTimeError: float = _MAX_REP_ERROR
14
+ preferContestLetter: bool = False
14
15
 
15
16
  def flags_with_defaults(self) -> typing.Dict[BocaLanguage, str]:
16
17
  res: typing.Dict[BocaLanguage, str] = {
@@ -6,10 +6,11 @@ from typing import List
6
6
  import typer
7
7
 
8
8
  from rbx import console
9
- from rbx.box import package
9
+ from rbx.box import naming, 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
 
@@ -47,14 +52,20 @@ class BocaPackager(BasePackager):
47
52
  raise typer.Exit(1)
48
53
 
49
54
  def _get_problem_name(self) -> str:
50
- pkg = package.find_problem_package_or_die()
51
55
  # BOCA forces Java class names to be the name of the problem.
52
- return pkg.name.replace('-', '_')
56
+ return self.package_basename().replace('-', '_')
57
+
58
+ def _get_problem_basename(self) -> str:
59
+ extension = get_extension_or_default('boca', BocaExtension)
60
+ shortname = naming.get_problem_shortname()
61
+ if extension.preferContestLetter and shortname is not None:
62
+ return shortname
63
+ return self._get_problem_name()
53
64
 
54
65
  def _get_problem_info(self) -> str:
55
66
  statement = self._get_main_statement()
56
67
  return (
57
- f'basename={self._get_problem_name()}\n'
68
+ f'basename={self._get_problem_basename()}\n'
58
69
  f'fullname={statement.title}\n'
59
70
  f'descfile={self._get_problem_name()}.pdf\n'
60
71
  )
@@ -119,7 +130,7 @@ class BocaPackager(BasePackager):
119
130
  )
120
131
 
121
132
  def _get_compare(self) -> str:
122
- compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare'
133
+ compare_path = get_default_app_path() / 'packagers' / 'boca' / 'compare.sh'
123
134
  if not compare_path.exists():
124
135
  console.console.print(
125
136
  '[error]BOCA template compare script not found.[/error]'
@@ -145,7 +156,26 @@ class BocaPackager(BasePackager):
145
156
  .replace('{{checker_content}}', checker)
146
157
  )
147
158
 
159
+ def _get_interactor(self) -> str:
160
+ extension = get_extension_or_default('boca', BocaExtension)
161
+
162
+ interactor_path = (
163
+ get_default_app_path() / 'packagers' / 'boca' / 'interactor_compile.sh'
164
+ )
165
+ if not interactor_path.exists():
166
+ console.console.print(
167
+ '[error]BOCA template interactor compile script not found.[/error]'
168
+ )
169
+ raise typer.Exit(1)
170
+
171
+ interactor_text = interactor_path.read_text()
172
+ interactor = package.get_interactor().path.read_text()
173
+ return interactor_text.replace(
174
+ '{{rbxFlags}}', extension.flags_with_defaults()['cc']
175
+ ).replace('{{interactor_content}}', interactor)
176
+
148
177
  def _get_compile(self, language: BocaLanguage) -> str:
178
+ pkg = package.find_problem_package_or_die()
149
179
  extension = get_extension_or_default('boca', BocaExtension)
150
180
 
151
181
  compile_path = (
@@ -160,6 +190,10 @@ class BocaPackager(BasePackager):
160
190
  compile_text = compile_path.read_text()
161
191
 
162
192
  assert 'umask 0022' in compile_text
193
+ if pkg.type == TaskType.COMMUNICATION:
194
+ compile_text = compile_text.replace(
195
+ 'umask 0022', 'umask 0022\n\n' + self._get_interactor()
196
+ )
163
197
  compile_text = compile_text.replace(
164
198
  'umask 0022', 'umask 0022\n\n' + self._get_checker()
165
199
  )
@@ -197,7 +231,8 @@ class BocaPackager(BasePackager):
197
231
  )
198
232
  )
199
233
 
200
- def name(self) -> str:
234
+ @classmethod
235
+ def name(cls) -> str:
201
236
  return 'boca'
202
237
 
203
238
  def package(
@@ -207,7 +242,7 @@ class BocaPackager(BasePackager):
207
242
  built_statements: List[BuiltStatement],
208
243
  ) -> pathlib.Path:
209
244
  extension = get_extension_or_default('boca', BocaExtension)
210
-
245
+ pkg = package.find_problem_package_or_die()
211
246
  # Prepare limits
212
247
  limits_path = into_path / 'limits'
213
248
  limits_path.mkdir(parents=True, exist_ok=True)
@@ -227,9 +262,17 @@ class BocaPackager(BasePackager):
227
262
  run_orig_path = (
228
263
  get_default_app_path() / 'packagers' / 'boca' / 'run' / language
229
264
  )
265
+ if pkg.type == TaskType.COMMUNICATION:
266
+ run_orig_path = (
267
+ get_default_app_path()
268
+ / 'packagers'
269
+ / 'boca'
270
+ / 'interactive'
271
+ / language
272
+ )
230
273
  if not run_orig_path.is_file():
231
274
  console.console.print(
232
- f'[error]Run script for language [item]{language}[/item] not found.[/error]'
275
+ f'[error]Run script for language [item]{language}[/item] not found for task of type [item]{pkg.type}[/item].[/error]'
233
276
  )
234
277
  raise typer.Exit(1)
235
278
  shutil.copyfile(run_orig_path, run_path / language)
@@ -268,11 +311,11 @@ class BocaPackager(BasePackager):
268
311
 
269
312
  testcases = self.get_flattened_built_testcases()
270
313
  for i, testcase in enumerate(testcases):
271
- shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
314
+ shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
272
315
  if testcase.outputPath is not None:
273
- shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
316
+ shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
274
317
  else:
275
- (outputs_path / f'{i+1:03d}').touch()
318
+ (outputs_path / f'{i + 1:03d}').touch()
276
319
 
277
320
  # Zip all.
278
321
  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,13 +20,9 @@ class MojPackager(BocaPackager):
20
20
  super().__init__()
21
21
  self.for_boca = for_boca
22
22
 
23
- def _get_problem_info(self) -> str:
24
- statement = self._get_main_statement()
25
- return (
26
- f'basename={self._get_problem_name()}\n'
27
- f'fullname={statement.title}\n'
28
- f'descfile={self._get_problem_name()}.pdf\n'
29
- )
23
+ @classmethod
24
+ def task_types(cls) -> List[TaskType]:
25
+ return [TaskType.COMMUNICATION, TaskType.BATCH]
30
26
 
31
27
  def _get_tl(self) -> str:
32
28
  extension = get_extension_or_default('boca', BocaExtension)
@@ -40,8 +36,12 @@ class MojPackager(BocaPackager):
40
36
  def _get_limits(self) -> str:
41
37
  pkg = package.find_problem_package_or_die()
42
38
  ml = pkg.memoryLimit
43
- ol = pkg.outputLimit
44
- return f'ULIMITS[-f]={ol}\n' f'ULIMITS[-v]={ml * 1024}\n'
39
+ # ol = pkg.outputLimit
40
+ limits = [
41
+ f'ULIMITS[-v]={ml * 1024}',
42
+ # f'ULIMITS[-f]={ol}',
43
+ ]
44
+ return '\n'.join(limits) + '\n'
45
45
 
46
46
  def _get_compare(self) -> str:
47
47
  extension = get_extension_or_default('boca', BocaExtension)
@@ -65,6 +65,33 @@ class MojPackager(BocaPackager):
65
65
  def _get_checker(self) -> str:
66
66
  return package.get_checker().path.read_text()
67
67
 
68
+ def _get_interactor(self) -> str:
69
+ return package.get_interactor().path.read_text()
70
+
71
+ def _expand_language_vars(self, language: BocaLanguage, dir: pathlib.Path):
72
+ extension = get_extension_or_default('boca', BocaExtension)
73
+
74
+ for path in dir.glob('**/*'):
75
+ if not path.is_file():
76
+ continue
77
+
78
+ replaced = path.read_text()
79
+ replaced = replaced.replace(
80
+ '{{rbxMaxMemory}}', f'{self._get_pkg_memorylimit(language)}'
81
+ ).replace(
82
+ '{{rbxInitialMemory}}',
83
+ f'{min(512, int(self._get_pkg_memorylimit(language) * 0.9))}',
84
+ )
85
+
86
+ flags = extension.flags_with_defaults()
87
+ if language in flags:
88
+ replaced = replaced.replace('{{rbxFlags}}', flags[language])
89
+
90
+ path.write_text(replaced)
91
+
92
+ if path.suffix == '.sh':
93
+ path.chmod(0o755)
94
+
68
95
  def _copy_solutions_moj(self, into_path: pathlib.Path):
69
96
  into_path = into_path / 'sols'
70
97
  has_good = False
@@ -83,7 +110,8 @@ class MojPackager(BocaPackager):
83
110
  console.console.print('[error]No good solution found.[/error]')
84
111
  raise typer.Exit(1)
85
112
 
86
- def name(self) -> str:
113
+ @classmethod
114
+ def name(cls) -> str:
87
115
  return 'moj'
88
116
 
89
117
  def package(
@@ -92,6 +120,8 @@ class MojPackager(BocaPackager):
92
120
  into_path: pathlib.Path,
93
121
  built_statements: List[BuiltStatement],
94
122
  ) -> pathlib.Path:
123
+ pkg = package.find_problem_package_or_die()
124
+
95
125
  # Prepare dummy files
96
126
  author_path = into_path / 'author'
97
127
  author_path.parent.mkdir(parents=True, exist_ok=True)
@@ -128,6 +158,48 @@ class MojPackager(BocaPackager):
128
158
  checker_path.parent.mkdir(parents=True, exist_ok=True)
129
159
  checker_path.write_text(self._get_checker())
130
160
 
161
+ # Prepare interactor
162
+ if pkg.type == TaskType.COMMUNICATION:
163
+ interactor_path = into_path / 'scripts' / 'interactor.cpp'
164
+ interactor_path.parent.mkdir(parents=True, exist_ok=True)
165
+ interactor_path.write_text(self._get_interactor())
166
+
167
+ interactor_prep_path = into_path / 'scripts' / 'interactor_prep.sh'
168
+ interactor_prep_path.parent.mkdir(parents=True, exist_ok=True)
169
+ shutil.copy(
170
+ get_default_app_path()
171
+ / 'packagers'
172
+ / 'moj'
173
+ / 'scripts'
174
+ / 'interactor_prep.sh',
175
+ interactor_prep_path,
176
+ )
177
+ interactor_prep_path.chmod(0o755)
178
+
179
+ interactor_run_path = into_path / 'scripts' / 'interactor_run.sh'
180
+ interactor_run_path.parent.mkdir(parents=True, exist_ok=True)
181
+ shutil.copy(
182
+ get_default_app_path()
183
+ / 'packagers'
184
+ / 'moj'
185
+ / 'scripts'
186
+ / 'interactor_run.sh',
187
+ interactor_run_path,
188
+ )
189
+ interactor_run_path.chmod(0o755)
190
+
191
+ # Prepare language scripts
192
+ extension = get_extension_or_default('boca', BocaExtension)
193
+ for language in extension.languages:
194
+ language_path = into_path / 'scripts' / language
195
+ language_path.parent.mkdir(parents=True, exist_ok=True)
196
+ src_path = (
197
+ get_default_app_path() / 'packagers' / 'moj' / 'scripts' / language
198
+ )
199
+ if src_path.exists():
200
+ shutil.copytree(src_path, language_path)
201
+ self._expand_language_vars(language, language_path)
202
+
131
203
  # Problem statement
132
204
  enunciado_path = into_path / 'docs' / 'enunciado.pdf'
133
205
  enunciado_path.parent.mkdir(parents=True, exist_ok=True)
@@ -150,15 +222,13 @@ class MojPackager(BocaPackager):
150
222
 
151
223
  testcases = self.get_flattened_built_testcases()
152
224
  for i, testcase in enumerate(testcases):
153
- shutil.copyfile(testcase.inputPath, inputs_path / f'{i+1:03d}')
225
+ shutil.copyfile(testcase.inputPath, inputs_path / f'{i + 1:03d}')
154
226
  if testcase.outputPath is not None:
155
- shutil.copyfile(testcase.outputPath, outputs_path / f'{i+1:03d}')
227
+ shutil.copyfile(testcase.outputPath, outputs_path / f'{i + 1:03d}')
156
228
  else:
157
- (outputs_path / f'{i+1:03d}').touch()
229
+ (outputs_path / f'{i + 1:03d}').touch()
158
230
 
159
231
  # Zip all.
160
- shutil.make_archive(
161
- str(build_path / self._get_problem_name()), 'zip', into_path
162
- )
232
+ shutil.make_archive(str(build_path / self.package_basename()), 'zip', into_path)
163
233
 
164
- return (build_path / self._get_problem_name()).with_suffix('.zip')
234
+ return (build_path / self.package_basename()).with_suffix('.zip')
@@ -3,11 +3,11 @@ import pathlib
3
3
  from abc import ABC, abstractmethod
4
4
  from typing import List, Tuple
5
5
 
6
- from rbx.box import package
6
+ from rbx.box import naming, 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
 
@@ -45,6 +50,13 @@ class BasePackager(ABC):
45
50
  res.add(statement.language)
46
51
  return list(res)
47
52
 
53
+ def package_basename(self):
54
+ pkg = package.find_problem_package_or_die()
55
+ shortname = naming.get_problem_shortname()
56
+ if shortname is not None:
57
+ return f'{shortname}-{pkg.name}'
58
+ return pkg.name
59
+
48
60
  def statement_types(self) -> List[StatementType]:
49
61
  return [StatementType.PDF]
50
62