rbx.cp 0.18.3__tar.gz → 0.18.5__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 (232) hide show
  1. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/PKG-INFO +1 -1
  2. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/pyproject.toml +1 -1
  3. rbx_cp-0.18.5/rbx/__version__.py +1 -0
  4. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/cli.py +7 -0
  5. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/code.py +26 -18
  6. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/build_contest_statements.py +21 -15
  7. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/environment.py +1 -1
  8. rbx_cp-0.18.5/rbx/box/exception.py +71 -0
  9. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/generators.py +16 -10
  10. rbx_cp-0.18.5/rbx/box/main.py +17 -0
  11. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/solutions.py +75 -23
  12. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/latex.py +5 -4
  13. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/stresses.py +29 -11
  14. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/console.py +4 -0
  15. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/sandboxes/tee.py +8 -2
  16. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/steps.py +18 -12
  17. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/steps_with_caching.py +14 -10
  18. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/env.rbx.yml +1 -1
  19. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/preset.rbx.yml +1 -1
  20. rbx_cp-0.18.3/rbx/__version__.py +0 -1
  21. rbx_cp-0.18.3/rbx/box/main.py +0 -6
  22. rbx_cp-0.18.3/rbx/resources/envs/default.rbx.yml +0 -67
  23. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/LICENSE +0 -0
  24. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/README.md +0 -0
  25. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/__init__.py +0 -0
  26. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/annotations.py +0 -0
  27. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/autoenum.py +0 -0
  28. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/__init__.py +0 -0
  29. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/builder.py +0 -0
  30. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/cd.py +0 -0
  31. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/checkers.py +0 -0
  32. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/compile.py +0 -0
  33. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/__init__.py +0 -0
  34. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/contest_package.py +0 -0
  35. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/contest_utils.py +0 -0
  36. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/main.py +0 -0
  37. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/schema.py +0 -0
  38. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/contest/statements.py +0 -0
  39. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/creation.py +0 -0
  40. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/deferred.py +0 -0
  41. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/download.py +0 -0
  42. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/dump_schemas.py +0 -0
  43. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/extensions.py +0 -0
  44. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/fields.py +0 -0
  45. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/formatting.py +0 -0
  46. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/git_utils.py +0 -0
  47. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/global_package.py +0 -0
  48. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/header.py +0 -0
  49. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/lang.py +0 -0
  50. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/lazy_importing_main.py +0 -0
  51. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/limits_info.py +0 -0
  52. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/linting.py +0 -0
  53. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/naming.py +0 -0
  54. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/package.py +0 -0
  55. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/__init__.py +0 -0
  56. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/boca/__init__.py +0 -0
  57. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/boca/boca_language_utils.py +0 -0
  58. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/boca/boca_outcome_utils.py +0 -0
  59. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/boca/extension.py +0 -0
  60. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/boca/packager.py +0 -0
  61. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/contest_main.py +0 -0
  62. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/importer.py +0 -0
  63. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/main.py +0 -0
  64. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/moj/packager.py +0 -0
  65. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/packager.py +0 -0
  66. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/pkg/packager.py +0 -0
  67. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/importer.py +0 -0
  68. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/packager.py +0 -0
  69. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  70. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/test.py +0 -0
  71. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/upload.py +0 -0
  72. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  73. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/presets/__init__.py +0 -0
  74. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/presets/fetch.py +0 -0
  75. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/presets/lock_schema.py +0 -0
  76. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/presets/schema.py +0 -0
  77. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/remote.py +0 -0
  78. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/retries.py +0 -0
  79. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/sanitizers/issue_stack.py +0 -0
  80. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/sanitizers/warning_stack.py +0 -0
  81. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/schema.py +0 -0
  82. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/setter_config.py +0 -0
  83. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/state.py +0 -0
  84. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/__init__.py +0 -0
  85. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/build_statements.py +0 -0
  86. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/builders.py +0 -0
  87. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/expander.py +0 -0
  88. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/joiners.py +0 -0
  89. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/latex_jinja.py +0 -0
  90. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/statements/schema.py +0 -0
  91. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/stats.py +0 -0
  92. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/stressing/__init__.py +0 -0
  93. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/stressing/finder_parser.py +0 -0
  94. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/stressing/generator_parser.py +0 -0
  95. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tasks.py +0 -0
  96. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testcase_extractors.py +0 -0
  97. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testcase_utils.py +0 -0
  98. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testcases/__init__.py +0 -0
  99. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testcases/main.py +0 -0
  100. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testing/__init__.py +0 -0
  101. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testing/testing_package.py +0 -0
  102. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testing/testing_preset.py +0 -0
  103. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/testing/testing_shared.py +0 -0
  104. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/timing.py +0 -0
  105. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/__init__.py +0 -0
  106. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/__init__.py +0 -0
  107. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/debug_utils.py +0 -0
  108. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/main.py +0 -0
  109. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/manual_scrape.py +0 -0
  110. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/scrape.py +0 -0
  111. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/boca/scraper.py +0 -0
  112. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/converter.py +0 -0
  113. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/tooling/main.py +0 -0
  114. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/__init__.py +0 -0
  115. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/captured_log.py +0 -0
  116. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/css/app.tcss +0 -0
  117. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/main.py +0 -0
  118. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/__init__.py +0 -0
  119. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/build.py +0 -0
  120. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/command.py +0 -0
  121. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/differ.py +0 -0
  122. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/error.py +0 -0
  123. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  124. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/run.py +0 -0
  125. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/run_explorer.py +0 -0
  126. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  127. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/selector.py +0 -0
  128. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/screens/test_explorer.py +0 -0
  129. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/utils/__init__.py +0 -0
  130. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/utils/run_ui.py +0 -0
  131. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/__init__.py +0 -0
  132. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/diff_box.py +0 -0
  133. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/file_log.py +0 -0
  134. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/interaction_box.py +0 -0
  135. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  136. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/test_output_box.py +0 -0
  137. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  138. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/unit.py +0 -0
  139. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/box/validators.py +0 -0
  140. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/config.py +0 -0
  141. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/__init__.py +0 -0
  142. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/caching.py +0 -0
  143. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/debug_context.py +0 -0
  144. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/grading_context.py +0 -0
  145. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/__init__.py +0 -0
  146. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/cacher.py +0 -0
  147. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/digester.py +0 -0
  148. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/program.py +0 -0
  149. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/sandbox.py +0 -0
  150. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  151. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  152. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/judge/storage.py +0 -0
  153. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/limits.py +0 -0
  154. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/grading/profiling.py +0 -0
  155. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/checkers/boilerplate.cpp +0 -0
  156. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/checkers/noop.cpp +0 -0
  157. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/default_config.json +0 -0
  158. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/default_setter_config.mac.yml +0 -0
  159. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/default_setter_config.yml +0 -0
  160. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/checker.sh +0 -0
  161. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compare.sh +0 -0
  162. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/c +0 -0
  163. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/cc +0 -0
  164. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/cpp +0 -0
  165. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/java +0 -0
  166. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/kt +0 -0
  167. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/py2 +0 -0
  168. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/compile/py3 +0 -0
  169. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/c +0 -0
  170. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/cc +0 -0
  171. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  172. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/java +0 -0
  173. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/kt +0 -0
  174. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  175. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  176. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  177. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/interactor_run.sh +0 -0
  178. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/bkp +0 -0
  179. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/c +0 -0
  180. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/cc +0 -0
  181. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/cpp +0 -0
  182. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/java +0 -0
  183. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/kt +0 -0
  184. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/py2 +0 -0
  185. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/run/py3 +0 -0
  186. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/safeexec.c +0 -0
  187. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/boca/safeexec_compile.sh +0 -0
  188. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  189. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  190. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  191. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  192. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  193. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  194. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  195. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  196. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  197. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  198. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  199. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  200. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  201. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  202. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  203. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  204. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  205. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  206. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/.gitignore +0 -0
  207. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  208. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  209. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/statement/info.rbx.tex +0 -0
  210. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/statement/instructions.tex +0 -0
  211. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/contest/statement/logo.png +0 -0
  212. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/.gitignore +0 -0
  213. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/gens/gen.cpp +0 -0
  214. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/manual_tests/samples/000.in +0 -0
  215. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/manual_tests/samples/001.in +0 -0
  216. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  217. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/rbx.h +0 -0
  218. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  219. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/sols/wa-overflow.cpp +0 -0
  220. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  221. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/testlib.h +0 -0
  222. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/testplan/random.py +0 -0
  223. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/testplan/random.txt +0 -0
  224. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  225. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  226. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/shared/contest_template.rbx.tex +0 -0
  227. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/shared/icpc.sty +0 -0
  228. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/presets/default/shared/problem_template.rbx.tex +0 -0
  229. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/templates/rbx.h +0 -0
  230. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/resources/templates/template.cpp +0 -0
  231. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/testing_utils.py +0 -0
  232. {rbx_cp-0.18.3 → rbx_cp-0.18.5}/rbx/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.18.3
3
+ Version: 0.18.5
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.18.3"
3
+ version = "0.18.5"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -0,0 +1 @@
1
+ __version__ = '0.18.5'
@@ -680,6 +680,12 @@ async def stress(
680
680
  help='Whether to print descriptors of the stress test.',
681
681
  hidden=True,
682
682
  ),
683
+ skip_invalid_testcases: bool = typer.Option(
684
+ False,
685
+ '--skip-invalid',
686
+ '--skip',
687
+ help='Whether to skip invalid testcases.',
688
+ ),
683
689
  ):
684
690
  if finder and not generator_args or generator_args and not finder:
685
691
  console.console.print(
@@ -701,6 +707,7 @@ async def stress(
701
707
  verbose=verbose,
702
708
  sanitized=sanitized,
703
709
  print_descriptors=print_descriptors,
710
+ skip_invalid_testcases=skip_invalid_testcases,
704
711
  )
705
712
 
706
713
  stresses.print_stress_report(report)
@@ -443,17 +443,19 @@ def _precompile_header(
443
443
  )
444
444
 
445
445
  with profiling.PushContext('code.precompile_header'):
446
- if not steps_with_caching.compile(
447
- commands,
448
- params=sandbox_params,
449
- artifacts=precompilation_artifacts,
450
- sandbox=sandbox,
451
- dependency_cache=dependency_cache,
452
- ):
453
- console.console.print(
446
+ try:
447
+ steps_with_caching.compile(
448
+ commands,
449
+ params=sandbox_params,
450
+ artifacts=precompilation_artifacts,
451
+ sandbox=sandbox,
452
+ dependency_cache=dependency_cache,
453
+ )
454
+ except steps.CompilationError as e:
455
+ e.print(
454
456
  f'[error]Failed to precompile header file: [item]{input_artifact.src}[/item][/error]'
455
457
  )
456
- raise typer.Exit(1)
458
+ raise
457
459
 
458
460
  if verbose:
459
461
  console.console.print(
@@ -487,13 +489,14 @@ def _precompile_header(
487
489
  ):
488
490
  input = DigestOrSource.create(local_cacher.put_file_from_fobj(f))
489
491
 
490
- return GradingFileInput(
492
+ res = GradingFileInput(
491
493
  **input.expand(),
492
494
  dest=input_artifact.dest.with_suffix('.h.gch'),
493
495
  # Do not track fingerprint of the precompiled header file,
494
496
  # trust the compilation step above.
495
497
  hash=False,
496
498
  )
499
+ return res
497
500
 
498
501
 
499
502
  # Compile code item and return its digest in the storage.
@@ -609,14 +612,19 @@ def compile_item(
609
612
  grading_context.CacheLevel.NO_CACHE,
610
613
  when=lambda: is_path_remote(code.path),
611
614
  ):
612
- if not steps_with_caching.compile(
613
- commands,
614
- params=sandbox_params,
615
- artifacts=artifacts,
616
- sandbox=sandbox,
617
- dependency_cache=dependency_cache,
618
- ):
619
- raise typer.Exit(1)
615
+ try:
616
+ steps_with_caching.compile(
617
+ commands,
618
+ params=sandbox_params,
619
+ artifacts=artifacts,
620
+ sandbox=sandbox,
621
+ dependency_cache=dependency_cache,
622
+ )
623
+ except steps.CompilationError as e:
624
+ e.print(
625
+ f'[error]Failed to compile item: [item]{code.path}[/item][/error]'
626
+ )
627
+ raise
620
628
 
621
629
  assert compiled_digest.value is not None
622
630
 
@@ -1,5 +1,6 @@
1
1
  import dataclasses
2
2
  import pathlib
3
+ import shutil
3
4
  import tempfile
4
5
  import typing
5
6
  from typing import Any, Dict, List, Optional, Tuple
@@ -228,10 +229,10 @@ def _build_problem_statements(
228
229
  )
229
230
  issue_stack.add_issue(StatementBuildIssue(extracted_problem.problem))
230
231
  continue
231
- dest_dir = root / '.problems' / extracted_problem.problem.short_name
232
+ dest_dir = pathlib.Path('.problems') / extracted_problem.problem.short_name
232
233
  dest_path = dest_dir / f'statement{output_type.get_file_suffix()}'
233
- dest_dir.mkdir(parents=True, exist_ok=True)
234
- dest_path.write_bytes(content)
234
+ (root / dest_dir).mkdir(parents=True, exist_ok=True)
235
+ (root / dest_path).write_bytes(content)
235
236
 
236
237
  problem_assets = (
237
238
  get_relative_assets(
@@ -240,7 +241,7 @@ def _build_problem_statements(
240
241
  )
241
242
  + contest_assets
242
243
  )
243
- prepare_assets(problem_assets, dest_dir)
244
+ prepare_assets(problem_assets, root / dest_dir)
244
245
 
245
246
  res.append(dataclasses.replace(extracted_problem, built_statement=dest_path))
246
247
  return res
@@ -393,6 +394,10 @@ def build_statement_rooted(
393
394
  return last_content, last_output
394
395
 
395
396
 
397
+ def get_statement_build_dir(statement: ContestStatement) -> pathlib.Path:
398
+ return pathlib.Path('build') / 'statement_build' / statement.name
399
+
400
+
396
401
  def build_statement(
397
402
  statement: ContestStatement,
398
403
  contest: Contest,
@@ -401,17 +406,18 @@ def build_statement(
401
406
  custom_vars: Optional[Dict[str, Any]] = None,
402
407
  install_tex: bool = False,
403
408
  ) -> pathlib.Path:
404
- with tempfile.TemporaryDirectory() as td:
405
- root = pathlib.Path(td)
406
- last_content, last_output = build_statement_rooted(
407
- statement,
408
- contest,
409
- root,
410
- output_type=output_type,
411
- use_samples=use_samples,
412
- custom_vars=custom_vars,
413
- install_tex=install_tex,
414
- )
409
+ root = get_statement_build_dir(statement)
410
+ shutil.rmtree(root, ignore_errors=True)
411
+ root.mkdir(parents=True, exist_ok=True)
412
+ last_content, last_output = build_statement_rooted(
413
+ statement,
414
+ contest,
415
+ root,
416
+ output_type=output_type,
417
+ use_samples=use_samples,
418
+ custom_vars=custom_vars,
419
+ install_tex=install_tex,
420
+ )
415
421
 
416
422
  statement_path = (pathlib.Path('build') / statement.name).with_suffix(
417
423
  last_output.get_file_suffix()
@@ -252,7 +252,7 @@ execution config can be individually overridden in the language configuration.""
252
252
 
253
253
 
254
254
  def get_app_environment_path(env: str) -> pathlib.Path:
255
- return config.get_resources_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
255
+ return config.get_resources_file(pathlib.PosixPath('presets') / env / 'env.rbx.yml')
256
256
 
257
257
 
258
258
  def get_active_environment_path() -> pathlib.Path:
@@ -0,0 +1,71 @@
1
+ from typing import List, Optional
2
+
3
+ from rich.console import Capture, Console
4
+
5
+ from rbx import console
6
+
7
+
8
+ class PossiblyCapture:
9
+ def __init__(
10
+ self, console: Console, msg: List[str], capture: Optional[Capture] = None
11
+ ):
12
+ self.console = console
13
+ self.msg = msg
14
+ self.capture = capture
15
+ self.actual_capture = None
16
+
17
+ def __enter__(self):
18
+ if self.capture is not None:
19
+ return
20
+ self.actual_capture = self.console.capture()
21
+ self.actual_capture.__enter__()
22
+ return self
23
+
24
+ def __exit__(self, exc_type, exc_value, traceback):
25
+ if self.actual_capture is not None:
26
+ self.actual_capture.__exit__(exc_type, exc_value, traceback)
27
+ self.msg.append(self.actual_capture.get())
28
+ self.actual_capture = None
29
+
30
+
31
+ class RbxException(RuntimeError):
32
+ def __init__(self):
33
+ super().__init__()
34
+ self.msg = []
35
+ self.capture = None
36
+ self.console = console.new_console()
37
+
38
+ def possibly_capture(self):
39
+ return PossiblyCapture(self.console, self.msg, self.capture)
40
+
41
+ def rule(self, *args, **kwargs):
42
+ with self.possibly_capture():
43
+ self.console.rule(*args, **kwargs)
44
+
45
+ def print(self, *args, **kwargs):
46
+ with self.possibly_capture():
47
+ self.console.print(*args, **kwargs)
48
+
49
+ def log(self, *args, **kwargs):
50
+ with self.possibly_capture():
51
+ self.console.log(*args, **kwargs)
52
+
53
+ def __enter__(self):
54
+ capture = self.console.capture()
55
+ capture.__enter__()
56
+ self.capture = capture
57
+ return self
58
+
59
+ def __exit__(self, exc_type, exc_value, traceback):
60
+ if self.capture is not None:
61
+ self.capture.__exit__(exc_type, exc_value, traceback)
62
+ self.msg.append(self.capture.get())
63
+ self.capture = None
64
+ if exc_type is not None:
65
+ return
66
+ raise self
67
+
68
+ def __str__(self) -> str:
69
+ if not self.msg:
70
+ return ''
71
+ return ''.join(self.msg)
@@ -4,10 +4,12 @@ import shutil
4
4
  from typing import Dict, List, Optional, Set
5
5
 
6
6
  import typer
7
+ from rich.console import Console
7
8
 
8
9
  from rbx import console
9
10
  from rbx.box import checkers, package, testcase_utils, validators
10
11
  from rbx.box.code import SanitizationLevel, compile_item, run_item
12
+ from rbx.box.exception import RbxException
11
13
  from rbx.box.schema import (
12
14
  CodeItem,
13
15
  GeneratorCall,
@@ -37,6 +39,10 @@ from rbx.grading.steps import (
37
39
  from rbx.utils import StatusProgress
38
40
 
39
41
 
42
+ class ValidationError(RbxException):
43
+ pass
44
+
45
+
40
46
  def _compile_generator(generator: CodeItem) -> str:
41
47
  return compile_item(generator, sanitized=SanitizationLevel.PREFER)
42
48
 
@@ -261,7 +267,7 @@ async def generate_standalone(
261
267
  validator_digest: Optional[str] = None,
262
268
  progress: Optional[StatusProgress] = None,
263
269
  ):
264
- def _print_error_header(text: Optional[str] = None):
270
+ def _print_error_header(console: Console, text: Optional[str] = None):
265
271
  prefix = 'Failed generating test'
266
272
  if group_entry is not None:
267
273
  prefix += (
@@ -271,11 +277,11 @@ async def generate_standalone(
271
277
  if text:
272
278
  suffix = f': {text}'
273
279
  if spec.generator_call is not None:
274
- console.console.print(
280
+ console.print(
275
281
  f'[error]{prefix} using generator call [info]{spec.generator_call.name} {spec.generator_call.args}[/info]{suffix}[/error]'
276
282
  )
277
283
  else:
278
- console.console.print(f'[error]{prefix}{suffix}[/error]')
284
+ console.print(f'[error]{prefix}{suffix}[/error]')
279
285
 
280
286
  if spec.generator_call is not None:
281
287
  call = spec.generator_call
@@ -301,7 +307,7 @@ async def generate_standalone(
301
307
  extra_args=call.args or None,
302
308
  )
303
309
  if not generation_log or generation_log.exitcode != 0:
304
- _print_error_header()
310
+ _print_error_header(console.console)
305
311
  if generation_log is not None:
306
312
  console.console.print(
307
313
  f'[error]Summary:[/error] {generation_log.get_summary()}'
@@ -333,12 +339,12 @@ async def generate_standalone(
333
339
  validator_digest,
334
340
  )
335
341
  if not validation_info.ok:
336
- _print_error_header('failed validating testcase.')
337
- console.console.print(f'[error]Message:[/error] {validation_info.message}')
338
- console.console.print(
339
- f'Testcase written at [item]{spec.copied_to.inputPath}[/item]'
340
- )
341
- raise typer.Exit(1)
342
+ with ValidationError() as err:
343
+ _print_error_header(err.console, 'failed validating testcase.')
344
+ err.print(f'[error]Message:[/error] {validation_info.message}')
345
+ err.print(
346
+ f'Testcase written at [item]{spec.copied_to.inputPath}[/item]'
347
+ )
342
348
 
343
349
 
344
350
  async def generate_testcases(
@@ -0,0 +1,17 @@
1
+ # flake8: noqa
2
+ import nest_asyncio
3
+ import sys
4
+
5
+ from rbx.box.exception import RbxException
6
+
7
+ nest_asyncio.apply()
8
+
9
+ from rbx.box.cli import app as app_cli
10
+
11
+
12
+ def app():
13
+ try:
14
+ app_cli()
15
+ except RbxException as e:
16
+ print(str(e))
17
+ sys.exit(1)
@@ -55,7 +55,7 @@ from rbx.box.testcase_utils import (
55
55
  parse_interaction,
56
56
  print_interaction,
57
57
  )
58
- from rbx.grading import grading_context
58
+ from rbx.grading import grading_context, steps
59
59
  from rbx.grading.limits import Limits
60
60
  from rbx.grading.steps import (
61
61
  Evaluation,
@@ -90,6 +90,7 @@ class SolutionReportSkeleton(BaseModel):
90
90
  entries: List[TestcaseEntry]
91
91
  groups: List[GroupSkeleton]
92
92
  limits: Dict[str, Limits]
93
+ compiled_solutions: Dict[str, str]
93
94
  verification: VerificationLevel
94
95
  capture_pipes: bool = False
95
96
 
@@ -125,6 +126,9 @@ class SolutionReportSkeleton(BaseModel):
125
126
  return i
126
127
  return None
127
128
 
129
+ def get_solution_compiled_digest(self, solution: Solution) -> str:
130
+ return self.compiled_solutions[str(solution.path)]
131
+
128
132
  def get_solution_path_set(self) -> Set[str]:
129
133
  return set(str(sol.path) for sol in self.solutions)
130
134
 
@@ -179,10 +183,22 @@ def get_exact_matching_solutions(expected_outcome: ExpectedOutcome) -> List[Solu
179
183
  return res
180
184
 
181
185
 
186
+ class FailedToCompileSolutionIssue(issue_stack.Issue):
187
+ def __init__(self, solution: Solution):
188
+ self.solution = solution
189
+
190
+ def get_detailed_section(self) -> Tuple[str, ...]:
191
+ return ('solutions',)
192
+
193
+ def get_detailed_message(self) -> str:
194
+ return f'[item]{href(self.solution.path)}[/item] could not be compiled and was skipped.'
195
+
196
+
182
197
  def compile_solutions(
183
198
  progress: Optional[StatusProgress] = None,
184
199
  tracked_solutions: Optional[Set[str]] = None,
185
200
  sanitized: bool = False,
201
+ fail_if_one: bool = True,
186
202
  ) -> Dict[pathlib.Path, str]:
187
203
  compiled_solutions = {}
188
204
 
@@ -199,6 +215,10 @@ def compile_solutions(
199
215
  if sanitized
200
216
  else SanitizationLevel.NONE,
201
217
  )
218
+ except steps.CompilationError:
219
+ if fail_if_one and len(tracked_solutions) <= 1:
220
+ raise
221
+ issue_stack.add_issue(FailedToCompileSolutionIssue(solution))
202
222
  except:
203
223
  console.console.print(
204
224
  f'[error]Failed compiling solution {href(solution.path)}.[/error]'
@@ -283,13 +303,48 @@ def _get_solutions_for_skeleton(
283
303
  return solutions
284
304
 
285
305
 
306
+ def _get_compiled_solutions_for_skeleton(
307
+ tracked_solutions: Optional[Iterable[str]] = None,
308
+ progress: Optional[StatusProgress] = None,
309
+ sanitized: bool = False,
310
+ verification: VerificationLevel = VerificationLevel.NONE,
311
+ ) -> Tuple[List[Solution], Dict[str, str]]:
312
+ solutions_to_compile = _get_solutions_for_skeleton(tracked_solutions, verification)
313
+
314
+ compiled_solutions = compile_solutions(
315
+ progress=progress,
316
+ tracked_solutions=set(str(solution.path) for solution in solutions_to_compile),
317
+ sanitized=sanitized,
318
+ )
319
+
320
+ # TODO: Handle solutions that failed to compile.
321
+ solutions = [
322
+ solution
323
+ for solution in solutions_to_compile
324
+ if solution.path in compiled_solutions
325
+ ]
326
+
327
+ return solutions, {
328
+ str(solution_path): digest
329
+ for solution_path, digest in compiled_solutions.items()
330
+ }
331
+
332
+
286
333
  def _get_report_skeleton(
287
334
  tracked_solutions: Optional[Iterable[str]] = None,
288
335
  verification: VerificationLevel = VerificationLevel.NONE,
289
336
  timelimit_override: Optional[int] = None,
337
+ progress: Optional[StatusProgress] = None,
338
+ sanitized: bool = False,
290
339
  ) -> SolutionReportSkeleton:
291
340
  pkg = package.find_problem_package_or_die()
292
- solutions = _get_solutions_for_skeleton(tracked_solutions, verification)
341
+
342
+ solutions, compiled_solutions = _get_compiled_solutions_for_skeleton(
343
+ tracked_solutions=tracked_solutions,
344
+ verification=verification,
345
+ progress=progress,
346
+ sanitized=sanitized,
347
+ )
293
348
 
294
349
  langs = set(find_language_name(solution) for solution in solutions)
295
350
  limits = {
@@ -324,6 +379,7 @@ def _get_report_skeleton(
324
379
  groups=groups,
325
380
  limits=limits,
326
381
  entries=entries,
382
+ compiled_solutions=compiled_solutions,
327
383
  verification=verification,
328
384
  capture_pipes=state.STATE.debug_logs,
329
385
  )
@@ -340,7 +396,6 @@ def _produce_solution_items(
340
396
  verification: VerificationLevel = VerificationLevel.NONE,
341
397
  check: bool = True,
342
398
  timelimit_override: Optional[int] = None,
343
- sanitized: bool = False,
344
399
  nruns: int = 0,
345
400
  ) -> List[EvaluationItem]:
346
401
  pkg = package.find_problem_package_or_die()
@@ -354,12 +409,6 @@ def _produce_solution_items(
354
409
  checker_digest = checkers.compile_checker() if check else None
355
410
  interactor_digest = None
356
411
 
357
- compiled_solutions = compile_solutions(
358
- progress=progress,
359
- tracked_solutions=skeleton.get_solution_path_set(),
360
- sanitized=sanitized,
361
- )
362
-
363
412
  def yield_items(
364
413
  solution: SolutionSkeleton, group_name: str
365
414
  ) -> List[EvaluationItem]:
@@ -367,7 +416,7 @@ def _produce_solution_items(
367
416
  for i, eval in enumerate(
368
417
  _run_solution(
369
418
  solution,
370
- compiled_solutions[solution.path],
419
+ skeleton.get_solution_compiled_digest(solution),
371
420
  checker_digest,
372
421
  solution.runs_dir,
373
422
  group_name,
@@ -425,9 +474,11 @@ def run_solutions(
425
474
  nruns: int = 0,
426
475
  ) -> RunSolutionResult:
427
476
  skeleton = _get_report_skeleton(
428
- tracked_solutions,
477
+ progress=progress,
478
+ tracked_solutions=tracked_solutions,
429
479
  verification=verification,
430
480
  timelimit_override=timelimit_override,
481
+ sanitized=sanitized,
431
482
  )
432
483
  result = RunSolutionResult(
433
484
  skeleton=skeleton,
@@ -437,7 +488,6 @@ def run_solutions(
437
488
  verification=verification,
438
489
  check=check,
439
490
  timelimit_override=timelimit_override,
440
- sanitized=sanitized,
441
491
  nruns=nruns,
442
492
  ),
443
493
  )
@@ -588,7 +638,6 @@ def _run_interactive_solutions(
588
638
  progress: Optional[StatusProgress] = None,
589
639
  verification: VerificationLevel = VerificationLevel.NONE,
590
640
  check: bool = True,
591
- sanitized: bool = False,
592
641
  ) -> Iterator[EvaluationItem]:
593
642
  pkg = package.find_problem_package_or_die()
594
643
 
@@ -599,12 +648,6 @@ def _run_interactive_solutions(
599
648
  checker_digest = checkers.compile_checker() if check else None
600
649
  interactor_digest = None
601
650
 
602
- compiled_solutions = compile_solutions(
603
- progress=progress,
604
- tracked_solutions=skeleton.get_solution_path_set(),
605
- sanitized=sanitized,
606
- )
607
-
608
651
  if progress:
609
652
  progress.update('Running solutions...')
610
653
 
@@ -614,7 +657,7 @@ def _run_interactive_solutions(
614
657
  async def run_fn(solution=solution, output_dir=output_dir):
615
658
  return await run_solution_on_testcase(
616
659
  solution,
617
- compiled_solutions[solution.path],
660
+ skeleton.get_solution_compiled_digest(solution),
618
661
  checker_digest,
619
662
  testcase,
620
663
  output_dir=output_dir,
@@ -632,9 +675,16 @@ def _run_interactive_solutions(
632
675
 
633
676
  def _get_interactive_skeleton(
634
677
  tracked_solutions: Optional[Iterable[str]] = None,
678
+ progress: Optional[StatusProgress] = None,
679
+ sanitized: bool = False,
635
680
  verification: VerificationLevel = VerificationLevel.NONE,
636
681
  ) -> SolutionReportSkeleton:
637
- solutions = _get_solutions_for_skeleton(tracked_solutions, verification)
682
+ solutions, compiled_solutions = _get_compiled_solutions_for_skeleton(
683
+ tracked_solutions,
684
+ verification=verification,
685
+ progress=progress,
686
+ sanitized=sanitized,
687
+ )
638
688
 
639
689
  langs = set(find_language_name(solution) for solution in solutions)
640
690
  limits = {
@@ -660,6 +710,7 @@ def _get_interactive_skeleton(
660
710
  limits=limits,
661
711
  entries=[],
662
712
  verification=verification,
713
+ compiled_solutions=compiled_solutions,
663
714
  capture_pipes=True,
664
715
  )
665
716
 
@@ -682,8 +733,10 @@ async def run_and_print_interactive_solutions(
682
733
  ):
683
734
  pkg = package.find_problem_package_or_die()
684
735
  skeleton = _get_interactive_skeleton(
685
- tracked_solutions,
736
+ tracked_solutions=tracked_solutions,
686
737
  verification=verification,
738
+ sanitized=sanitized,
739
+ progress=progress,
687
740
  )
688
741
 
689
742
  should_cache = testcase_entry is not None
@@ -705,7 +758,6 @@ async def run_and_print_interactive_solutions(
705
758
  progress=progress,
706
759
  verification=verification,
707
760
  check=check,
708
- sanitized=sanitized,
709
761
  )
710
762
 
711
763
  for item in items:
@@ -37,16 +37,17 @@ class Latex:
37
37
  self.latex = latex
38
38
 
39
39
  def build_pdf(self, temp_dir: pathlib.Path) -> LatexResult:
40
- temp_path = temp_dir / 'statement.tex'
40
+ temp_path = pathlib.Path('statement.tex')
41
41
  output_path = temp_path.with_suffix('.pdf')
42
42
  args = ['pdflatex', '-interaction', 'nonstopmode', str(temp_path)]
43
- temp_path.write_text(self.latex)
43
+
44
+ (temp_dir / temp_path).write_text(self.latex)
44
45
 
45
46
  completed = subprocess.run(args, timeout=15, capture_output=True, cwd=temp_dir)
46
- if completed.returncode != 0 or not output_path.exists():
47
+ if completed.returncode != 0 or not (temp_dir / output_path).exists():
47
48
  return LatexResult(result=completed, pdf=None)
48
49
 
49
- return LatexResult(result=completed, pdf=output_path.read_bytes())
50
+ return LatexResult(result=completed, pdf=(temp_dir / output_path).read_bytes())
50
51
 
51
52
 
52
53
  def install_tex_packages(path: pathlib.Path, cwd: pathlib.Path):