rbx.cp 0.9.0__tar.gz → 0.9.2__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 (246) hide show
  1. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/PKG-INFO +1 -1
  2. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/pyproject.toml +1 -1
  3. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/cli.py +3 -0
  4. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/environment.py +3 -3
  5. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/setter_config.py +18 -0
  6. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/config.py +7 -0
  7. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/caching.py +24 -2
  8. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/conftest.py +2 -2
  9. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/grading_context.py +34 -8
  10. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/steps_with_caching_run_test.py +281 -5
  11. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/default_setter_config.mac.yml +15 -0
  12. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/default_setter_config.yml +15 -0
  13. rbx_cp-0.9.2/rbx/resources/presets/default/env.rbx.yml +67 -0
  14. rbx_cp-0.9.0/rbx/resources/envs/default.rbx.yml +0 -36
  15. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/LICENSE +0 -0
  16. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/README.md +0 -0
  17. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/__init__.py +0 -0
  18. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/annotations.py +0 -0
  19. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/autoenum.py +0 -0
  20. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/__init__.py +0 -0
  21. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/builder.py +0 -0
  22. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/cd.py +0 -0
  23. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/checkers.py +0 -0
  24. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/code.py +0 -0
  25. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/compile.py +0 -0
  26. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/conftest.py +0 -0
  27. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/__init__.py +0 -0
  28. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/build_contest_statements.py +0 -0
  29. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/contest_package.py +0 -0
  30. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/contest_utils.py +0 -0
  31. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/main.py +0 -0
  32. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/schema.py +0 -0
  33. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/contest/statements.py +0 -0
  34. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/creation.py +0 -0
  35. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/deferred.py +0 -0
  36. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/download.py +0 -0
  37. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/dump_schemas.py +0 -0
  38. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/extensions.py +0 -0
  39. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/fields.py +0 -0
  40. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/formatting.py +0 -0
  41. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/generators.py +0 -0
  42. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/generators_test.py +0 -0
  43. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/git_utils.py +0 -0
  44. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/global_package.py +0 -0
  45. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/header.py +0 -0
  46. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/lang.py +0 -0
  47. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/lazy_importing_main.py +0 -0
  48. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/lazy_importing_test.py +0 -0
  49. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/linting.py +0 -0
  50. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/main.py +0 -0
  51. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/naming.py +0 -0
  52. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/package.py +0 -0
  53. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/__init__.py +0 -0
  54. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/boca/__init__.py +0 -0
  55. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/boca/extension.py +0 -0
  56. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/boca/packager.py +0 -0
  57. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/contest_main.py +0 -0
  58. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/main.py +0 -0
  59. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/moj/packager.py +0 -0
  60. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/packager.py +0 -0
  61. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/pkg/packager.py +0 -0
  62. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/polygon/packager.py +0 -0
  63. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  64. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/polygon/test.py +0 -0
  65. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/polygon/upload.py +0 -0
  66. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  67. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/presets/__init__.py +0 -0
  68. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/presets/fetch.py +0 -0
  69. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/presets/lock_schema.py +0 -0
  70. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/presets/schema.py +0 -0
  71. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/remote.py +0 -0
  72. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/retries.py +0 -0
  73. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/sanitizers/warning_stack.py +0 -0
  74. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/schema.py +0 -0
  75. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/solutions.py +0 -0
  76. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/solutions_test.py +0 -0
  77. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/state.py +0 -0
  78. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/__init__.py +0 -0
  79. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/build_statements.py +0 -0
  80. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/builders.py +0 -0
  81. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/expander.py +0 -0
  82. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/joiners.py +0 -0
  83. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/latex.py +0 -0
  84. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/latex_jinja.py +0 -0
  85. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/statements/schema.py +0 -0
  86. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/stats.py +0 -0
  87. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/stresses.py +0 -0
  88. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/stressing/__init__.py +0 -0
  89. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/stressing/finder_parser.py +0 -0
  90. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/stressing/generator_parser.py +0 -0
  91. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tasks.py +0 -0
  92. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/testcase_extractors.py +0 -0
  93. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/testcase_utils.py +0 -0
  94. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/testcases/__init__.py +0 -0
  95. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/testcases/main.py +0 -0
  96. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/__init__.py +0 -0
  97. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/boca/__init__.py +0 -0
  98. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/boca/main.py +0 -0
  99. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/boca/scrape.py +0 -0
  100. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/boca/scraper.py +0 -0
  101. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/tooling/main.py +0 -0
  102. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/__init__.py +0 -0
  103. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/captured_log.py +0 -0
  104. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/css/app.tcss +0 -0
  105. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/main.py +0 -0
  106. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/__init__.py +0 -0
  107. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/build.py +0 -0
  108. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/command.py +0 -0
  109. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/differ.py +0 -0
  110. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/error.py +0 -0
  111. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  112. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/run.py +0 -0
  113. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/run_explorer.py +0 -0
  114. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  115. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/selector.py +0 -0
  116. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/screens/test_explorer.py +0 -0
  117. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/utils/__init__.py +0 -0
  118. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/utils/run_ui.py +0 -0
  119. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/__init__.py +0 -0
  120. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/diff_box.py +0 -0
  121. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/file_log.py +0 -0
  122. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/interaction_box.py +0 -0
  123. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  124. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/test_output_box.py +0 -0
  125. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  126. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/unit.py +0 -0
  127. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/validators.py +0 -0
  128. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/box/validators_test.py +0 -0
  129. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/checker.py +0 -0
  130. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/clone.py +0 -0
  131. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/conftest.py +0 -0
  132. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/console.py +0 -0
  133. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/create.py +0 -0
  134. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/edit.py +0 -0
  135. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/__init__.py +0 -0
  136. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/debug_context.py +0 -0
  137. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/__init__.py +0 -0
  138. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/cacher.py +0 -0
  139. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/digester.py +0 -0
  140. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/sandbox.py +0 -0
  141. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  142. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  143. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  144. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  145. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/storage.py +0 -0
  146. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/test.py +0 -0
  147. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/judge/testiso.py +0 -0
  148. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/limits.py +0 -0
  149. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/processing_context.py +0 -0
  150. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/profiling.py +0 -0
  151. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/steps.py +0 -0
  152. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading/steps_with_caching.py +0 -0
  153. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/grading_utils.py +0 -0
  154. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/hydration.py +0 -0
  155. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/main.py +0 -0
  156. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/metadata.py +0 -0
  157. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/providers/__init__.py +0 -0
  158. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/providers/codeforces.py +0 -0
  159. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/providers/provider.py +0 -0
  160. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/checkers/boilerplate.cpp +0 -0
  161. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/checkers/noop.cpp +0 -0
  162. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/default_config.json +0 -0
  163. /rbx_cp-0.9.0/rbx/resources/presets/default/env.rbx.yml → /rbx_cp-0.9.2/rbx/resources/envs/default.rbx.yml +0 -0
  164. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/envs/isolate.rbx.yml +0 -0
  165. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/checker.sh +0 -0
  166. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compare.sh +0 -0
  167. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/c +0 -0
  168. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/cc +0 -0
  169. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/cpp +0 -0
  170. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/java +0 -0
  171. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/kt +0 -0
  172. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/py2 +0 -0
  173. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/compile/py3 +0 -0
  174. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/c +0 -0
  175. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/cc +0 -0
  176. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  177. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/java +0 -0
  178. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/kt +0 -0
  179. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  180. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  181. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  182. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/interactor_run.sh +0 -0
  183. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/bkp +0 -0
  184. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/c +0 -0
  185. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/cc +0 -0
  186. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/cpp +0 -0
  187. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/java +0 -0
  188. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/kt +0 -0
  189. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/py2 +0 -0
  190. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/run/py3 +0 -0
  191. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/safeexec.c +0 -0
  192. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/boca/safeexec_compile.sh +0 -0
  193. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  194. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  195. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  196. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  197. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  198. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  199. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  200. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  201. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  202. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  203. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  204. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  205. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  206. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  207. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  208. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  209. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  210. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  211. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/contest/.gitignore +0 -0
  212. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  213. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  214. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/contest/statement/instructions.tex +0 -0
  215. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/contest/statement/logo.png +0 -0
  216. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  217. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/.gitignore +0 -0
  218. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/gens/gen.cpp +0 -0
  219. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/manual_tests/samples/000.in +0 -0
  220. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/manual_tests/samples/001.in +0 -0
  221. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  222. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/rbx.h +0 -0
  223. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  224. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  225. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  226. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  227. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/testplan/random.py +0 -0
  228. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/testplan/random.txt +0 -0
  229. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  230. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  231. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/shared/contest_template.rbx.tex +0 -0
  232. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/shared/icpc.sty +0 -0
  233. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/presets/default/shared/problem_template.rbx.tex +0 -0
  234. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/templates/rbx.h +0 -0
  235. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/resources/templates/template.cpp +0 -0
  236. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/run.py +0 -0
  237. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/schema.py +0 -0
  238. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/submit.py +0 -0
  239. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/submitors/__init__.py +0 -0
  240. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/submitors/codeforces.py +0 -0
  241. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/submitors/submitor.py +0 -0
  242. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/test.py +0 -0
  243. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/testcase.py +0 -0
  244. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/testcase_rendering.py +0 -0
  245. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/testing_utils.py +0 -0
  246. {rbx_cp-0.9.0 → rbx_cp-0.9.2}/rbx/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.9.0
3
+ Version: 0.9.2
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.9.0"
3
+ version = "0.9.2"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -159,6 +159,9 @@ def main(
159
159
  state.STATE.debug_logs = capture
160
160
 
161
161
  grading_context.cache_level_var.set(grading_context.CacheLevel(cache))
162
+ grading_context.check_integrity_var.set(
163
+ setter_config.get_setter_config().caching.check_integrity
164
+ )
162
165
 
163
166
  if profile:
164
167
  from rbx.grading import profiling
@@ -247,13 +247,13 @@ execution config can be individually overridden in the language configuration.""
247
247
 
248
248
 
249
249
  def get_app_environment_path(env: str) -> pathlib.Path:
250
- return config.get_app_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
250
+ return config.get_resources_file(pathlib.PosixPath('envs') / f'{env}.rbx.yml')
251
251
 
252
252
 
253
253
  def get_active_environment_path() -> pathlib.Path:
254
254
  env_path = presets.get_preset_environment_path()
255
255
  if env_path is None:
256
- env_path = get_app_environment_path(config.get_config().boxEnvironment)
256
+ env_path = get_app_environment_path('default')
257
257
  return env_path
258
258
 
259
259
 
@@ -261,7 +261,7 @@ def get_active_environment_path() -> pathlib.Path:
261
261
  def get_active_environment_description() -> str:
262
262
  env_path = presets.get_preset_environment_path()
263
263
  if env_path is None:
264
- return config.get_config().boxEnvironment
264
+ return 'default'
265
265
  preset = presets.get_active_preset()
266
266
  return f'preset - {preset.name}'
267
267
 
@@ -9,6 +9,7 @@ import typer
9
9
  from pydantic import BaseModel, Field
10
10
 
11
11
  from rbx import config, console, utils
12
+ from rbx.grading.grading_context import CacheLevel
12
13
 
13
14
  app = typer.Typer(no_args_is_help=True)
14
15
 
@@ -52,6 +53,19 @@ class RepeatsConfig(BaseModel):
52
53
  )
53
54
 
54
55
 
56
+ class CachingConfig(BaseModel):
57
+ level: CacheLevel = Field(
58
+ default=CacheLevel.CACHE_ALL,
59
+ description='Whether to enable caching and which caching level to use.',
60
+ )
61
+
62
+ check_integrity: bool = Field(
63
+ default=True,
64
+ description='Whether to check the integrity of the cached result, and evict it'
65
+ 'if file has changed since it was cached.',
66
+ )
67
+
68
+
55
69
  class SetterConfig(BaseModel):
56
70
  sanitizers: SanitizersConfig = Field(
57
71
  default_factory=SanitizersConfig, # type: ignore
@@ -75,6 +89,10 @@ class SetterConfig(BaseModel):
75
89
  default=True,
76
90
  description='Whether to use hyperlinks in the terminal output.',
77
91
  )
92
+ caching: CachingConfig = Field(
93
+ default_factory=CachingConfig, # type: ignore
94
+ description='Configuration for caching.',
95
+ )
78
96
 
79
97
  def substitute_command(self, command: str, sanitized: bool = False) -> str:
80
98
  exe = shlex.split(command)[0]
@@ -90,6 +90,13 @@ def get_empty_app_persist_path() -> pathlib.Path:
90
90
  return app_dir
91
91
 
92
92
 
93
+ def get_resources_file(path: pathlib.Path) -> pathlib.Path:
94
+ file_path = importlib.resources.files('rbx') / 'resources' / path # type: ignore
95
+ if file_path.is_file():
96
+ return file_path
97
+ raise FileNotFoundError(f'File {path} not found in {_RESOURCES_PKG}.')
98
+
99
+
93
100
  def get_app_file(path: pathlib.Path) -> pathlib.Path:
94
101
  file_path = get_app_path() / path
95
102
  if file_path.is_file():
@@ -15,7 +15,12 @@ from rbx.grading.judge.cacher import FileCacher
15
15
  from rbx.grading.judge.digester import digest_cooperatively
16
16
  from rbx.grading.judge.storage import copyfileobj
17
17
  from rbx.grading.profiling import Profiler
18
- from rbx.grading.steps import DigestHolder, GradingArtifacts, GradingLogsHolder
18
+ from rbx.grading.steps import (
19
+ DigestHolder,
20
+ GradingArtifacts,
21
+ GradingFileOutput,
22
+ GradingLogsHolder,
23
+ )
19
24
 
20
25
  VERBOSE = False
21
26
 
@@ -109,10 +114,27 @@ def _build_fingerprint_list(
109
114
  return fingerprints
110
115
 
111
116
 
117
+ def _maybe_check_integrity(output: GradingFileOutput):
118
+ if not grading_context.should_check_integrity():
119
+ return
120
+ if output.dest is None or not output.dest.is_symlink():
121
+ return
122
+ if output.digest is None or output.digest.value is None:
123
+ return
124
+ with output.dest.open('rb') as f:
125
+ fingerprint = digest_cooperatively(f)
126
+ if fingerprint != output.digest.value:
127
+ raise ValueError(
128
+ f'Cache was tampered with, file {output.dest} has changed since it was cached.\nPlease run `rbx clean` to reset the cache.'
129
+ )
130
+
131
+
112
132
  def _build_output_fingerprint_list(artifacts_list: List[GradingArtifacts]) -> List[str]:
113
133
  fingerprints = []
114
134
  for artifacts in artifacts_list:
115
135
  for output in artifacts.outputs:
136
+ if output.hash:
137
+ _maybe_check_integrity(output)
116
138
  if output.dest is None or output.intermediate or output.hash:
117
139
  continue
118
140
  if not output.dest.is_file():
@@ -313,7 +335,7 @@ class DependencyCache:
313
335
  self.cacher = cacher
314
336
  self.db = shelve.open(self._cache_name())
315
337
  tmp_dir = pathlib.Path(tempfile.mkdtemp())
316
- self.transient_db = shelve.open(tmp_dir / '.cache_db')
338
+ self.transient_db = shelve.open(str(tmp_dir / '.cache_db'))
317
339
  atexit.register(lambda: self.db.close())
318
340
  atexit.register(lambda: self.transient_db.close())
319
341
  atexit.register(lambda: shutil.rmtree(tmp_dir))
@@ -28,6 +28,6 @@ def sandbox(request, file_cacher: FileCacher) -> Iterator[SandboxBase]:
28
28
 
29
29
  @pytest.fixture
30
30
  def dependency_cache(
31
- request, cleandir: pathlib.Path, storage: Storage
31
+ request, cleandir: pathlib.Path, file_cacher: FileCacher
32
32
  ) -> Iterator[DependencyCache]:
33
- yield DependencyCache(cleandir / '.box', storage)
33
+ yield DependencyCache(cleandir / '.box', file_cacher)
@@ -1,7 +1,8 @@
1
1
  import contextvars
2
- from enum import Enum
3
2
  from typing import Callable, Optional, Union
4
3
 
4
+ from rbx.autoenum import AutoEnum, alias
5
+
5
6
  Condition = Union[bool, Callable[[], bool]]
6
7
 
7
8
 
@@ -15,11 +16,11 @@ class ConditionedContext:
15
16
  return self.when()
16
17
 
17
18
 
18
- class CacheLevel(Enum):
19
- NO_CACHE = 0
20
- CACHE_TRANSIENTLY = 1
21
- CACHE_COMPILATION = 2
22
- CACHE_ALL = 3
19
+ class CacheLevel(AutoEnum):
20
+ NO_CACHE = alias('none')
21
+ CACHE_TRANSIENTLY = alias('transient')
22
+ CACHE_COMPILATION = alias('compilation')
23
+ CACHE_ALL = alias('all')
23
24
 
24
25
 
25
26
  cache_level_var = contextvars.ContextVar('cache_level', default=CacheLevel.CACHE_ALL)
@@ -30,11 +31,11 @@ def is_compilation_only() -> bool:
30
31
 
31
32
 
32
33
  def is_transient() -> bool:
33
- return cache_level_var.get().value <= CacheLevel.CACHE_TRANSIENTLY.value
34
+ return cache_level_var.get() == CacheLevel.CACHE_TRANSIENTLY or is_no_cache()
34
35
 
35
36
 
36
37
  def is_no_cache() -> bool:
37
- return cache_level_var.get().value <= CacheLevel.NO_CACHE.value
38
+ return cache_level_var.get() == CacheLevel.NO_CACHE
38
39
 
39
40
 
40
41
  class cache_level(ConditionedContext):
@@ -94,3 +95,28 @@ class compression(ConditionedContext):
94
95
  if self.use_compression_token is not None:
95
96
  use_compression_var.reset(self.use_compression_token)
96
97
  return None
98
+
99
+
100
+ check_integrity_var = contextvars.ContextVar('check_integrity', default=True)
101
+
102
+
103
+ def should_check_integrity() -> bool:
104
+ return check_integrity_var.get()
105
+
106
+
107
+ class check_integrity(ConditionedContext):
108
+ def __init__(self, enabled: bool, when: Condition = True):
109
+ super().__init__(when)
110
+ self.enabled = enabled
111
+ self.token = None
112
+
113
+ def __enter__(self):
114
+ if not self.should_enter():
115
+ return self
116
+ self.token = check_integrity_var.set(self.enabled)
117
+ return self
118
+
119
+ def __exit__(self, exc_type, exc_val, exc_tb):
120
+ if self.token is not None:
121
+ check_integrity_var.reset(self.token)
122
+ return None
@@ -2,9 +2,12 @@ import os
2
2
  import pathlib
3
3
  import sys
4
4
 
5
- from rbx.grading import steps_with_caching
5
+ import pytest
6
+
7
+ from rbx.grading import grading_context, steps_with_caching
6
8
  from rbx.grading.caching import DependencyCache
7
9
  from rbx.grading.judge.cacher import FileCacher
10
+ from rbx.grading.judge.digester import digest_cooperatively
8
11
  from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
9
12
  from rbx.grading.steps import (
10
13
  DigestOrSource,
@@ -74,6 +77,124 @@ async def test_run_from_disk(
74
77
  assert not artifacts.logs.cached
75
78
 
76
79
 
80
+ async def test_run_reruns_if_cache_disabled(
81
+ cleandir: pathlib.Path,
82
+ dependency_cache: DependencyCache,
83
+ sandbox: SandboxBase,
84
+ file_cacher: FileCacher,
85
+ ):
86
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
87
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
88
+ artifacts = GradingArtifacts()
89
+ artifacts.inputs.append(
90
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
91
+ )
92
+ artifacts.outputs.append(
93
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
94
+ )
95
+ with grading_context.cache_level(grading_context.CacheLevel.NO_CACHE):
96
+ await steps_with_caching.run(
97
+ f'{sys.executable} executable.py',
98
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
99
+ sandbox=sandbox,
100
+ artifacts=artifacts,
101
+ dependency_cache=dependency_cache,
102
+ )
103
+ return artifacts
104
+
105
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
106
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
107
+ assert artifacts.logs is not None
108
+ assert not artifacts.logs.cached
109
+
110
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
111
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
112
+ assert another_artifacts.logs is not None
113
+ assert not another_artifacts.logs.cached
114
+
115
+
116
+ async def test_run_reruns_if_first_run_cache_disabled(
117
+ cleandir: pathlib.Path,
118
+ dependency_cache: DependencyCache,
119
+ sandbox: SandboxBase,
120
+ file_cacher: FileCacher,
121
+ ):
122
+ async def configure_and_run_with_dest(
123
+ dest: pathlib.Path, cache: bool = True
124
+ ) -> GradingArtifacts:
125
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
126
+ artifacts = GradingArtifacts()
127
+ artifacts.inputs.append(
128
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
129
+ )
130
+ artifacts.outputs.append(
131
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
132
+ )
133
+ with grading_context.cache_level(
134
+ grading_context.CacheLevel.NO_CACHE, when=cache
135
+ ):
136
+ await steps_with_caching.run(
137
+ f'{sys.executable} executable.py',
138
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
139
+ sandbox=sandbox,
140
+ artifacts=artifacts,
141
+ dependency_cache=dependency_cache,
142
+ )
143
+ return artifacts
144
+
145
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'), cache=False)
146
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
147
+ assert artifacts.logs is not None
148
+ assert not artifacts.logs.cached
149
+
150
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
151
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
152
+ assert another_artifacts.logs is not None
153
+ assert not another_artifacts.logs.cached
154
+
155
+
156
+ async def test_run_reruns_if_second_run_cache_disabled(
157
+ cleandir: pathlib.Path,
158
+ dependency_cache: DependencyCache,
159
+ sandbox: SandboxBase,
160
+ file_cacher: FileCacher,
161
+ ):
162
+ async def configure_and_run_with_dest(
163
+ dest: pathlib.Path, cache: bool = True
164
+ ) -> GradingArtifacts:
165
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
166
+ artifacts = GradingArtifacts()
167
+ artifacts.inputs.append(
168
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
169
+ )
170
+ artifacts.outputs.append(
171
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
172
+ )
173
+ with grading_context.cache_level(
174
+ grading_context.CacheLevel.NO_CACHE, when=cache
175
+ ):
176
+ await steps_with_caching.run(
177
+ f'{sys.executable} executable.py',
178
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
179
+ sandbox=sandbox,
180
+ artifacts=artifacts,
181
+ dependency_cache=dependency_cache,
182
+ )
183
+ return artifacts
184
+
185
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
186
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
187
+ assert artifacts.logs is not None
188
+ assert not artifacts.logs.cached
189
+
190
+ another_artifacts = await configure_and_run_with_dest(
191
+ pathlib.Path('out.txt'), cache=False
192
+ )
193
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
194
+ assert another_artifacts.logs is not None
195
+ assert not another_artifacts.logs.cached
196
+
197
+
77
198
  async def test_run_caches_intermediate_digest_if_dest_changes(
78
199
  cleandir: pathlib.Path,
79
200
  dependency_cache: DependencyCache,
@@ -106,12 +227,127 @@ async def test_run_caches_intermediate_digest_if_dest_changes(
106
227
  another_artifacts = await configure_and_run_with_dest(
107
228
  pathlib.Path('another-out.txt')
108
229
  )
230
+ assert (cleandir / 'another-out.txt').read_text().strip() == '5'
231
+ assert another_artifacts.logs is not None
232
+ assert another_artifacts.logs.cached
233
+
234
+
235
+ async def test_run_caches_intermediate_digest_if_dest_changes_and_cache_transient(
236
+ cleandir: pathlib.Path,
237
+ dependency_cache: DependencyCache,
238
+ sandbox: SandboxBase,
239
+ file_cacher: FileCacher,
240
+ ):
241
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
242
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
243
+ artifacts = GradingArtifacts()
244
+ artifacts.inputs.append(
245
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
246
+ )
247
+ artifacts.outputs.append(
248
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
249
+ )
250
+
251
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
252
+ await steps_with_caching.run(
253
+ f'{sys.executable} executable.py',
254
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
255
+ sandbox=sandbox,
256
+ artifacts=artifacts,
257
+ dependency_cache=dependency_cache,
258
+ )
259
+ return artifacts
260
+
261
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
262
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
263
+ assert artifacts.logs is not None
264
+ assert not artifacts.logs.cached
265
+
266
+ another_artifacts = await configure_and_run_with_dest(
267
+ pathlib.Path('another-out.txt')
268
+ )
269
+ assert (cleandir / 'another-out.txt').read_text().strip() == '5'
270
+ assert another_artifacts.logs is not None
271
+ assert another_artifacts.logs.cached
272
+
273
+
274
+ async def test_run_overwrites_changed_file_when_storage_value_is_changed_and_cache_transient(
275
+ cleandir: pathlib.Path,
276
+ dependency_cache: DependencyCache,
277
+ sandbox: SandboxBase,
278
+ file_cacher: FileCacher,
279
+ ):
280
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
281
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
282
+ artifacts = GradingArtifacts()
283
+ artifacts.inputs.append(
284
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
285
+ )
286
+ artifacts.outputs.append(
287
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
288
+ )
289
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
290
+ await steps_with_caching.run(
291
+ f'{sys.executable} executable.py',
292
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
293
+ sandbox=sandbox,
294
+ artifacts=artifacts,
295
+ dependency_cache=dependency_cache,
296
+ )
297
+ return artifacts
298
+
299
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
300
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
301
+ assert artifacts.logs is not None
302
+ assert not artifacts.logs.cached
303
+
304
+ pathlib.Path('out.txt').write_text('42')
305
+
306
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
307
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
308
+ assert another_artifacts.logs is not None
309
+ assert another_artifacts.logs.cached
310
+
311
+
312
+ async def test_run_overwrites_changed_file_when_file_deleted_and_cache_transient(
313
+ cleandir: pathlib.Path,
314
+ dependency_cache: DependencyCache,
315
+ sandbox: SandboxBase,
316
+ file_cacher: FileCacher,
317
+ ):
318
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
319
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
320
+ artifacts = GradingArtifacts()
321
+ artifacts.inputs.append(
322
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
323
+ )
324
+ artifacts.outputs.append(
325
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
326
+ )
327
+ with grading_context.cache_level(grading_context.CacheLevel.CACHE_TRANSIENTLY):
328
+ await steps_with_caching.run(
329
+ f'{sys.executable} executable.py',
330
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
331
+ sandbox=sandbox,
332
+ artifacts=artifacts,
333
+ dependency_cache=dependency_cache,
334
+ )
335
+ return artifacts
336
+
337
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
338
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
339
+ assert artifacts.logs is not None
340
+ assert not artifacts.logs.cached
341
+
342
+ pathlib.Path('out.txt').unlink()
343
+
344
+ another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
109
345
  assert (cleandir / 'out.txt').read_text().strip() == '5'
110
346
  assert another_artifacts.logs is not None
111
347
  assert another_artifacts.logs.cached
112
348
 
113
349
 
114
- async def test_run_overwrite_changed_file_with_storage_value(
350
+ async def test_run_fails_when_storage_value_is_changed_and_integrity_check_is_enabled(
115
351
  cleandir: pathlib.Path,
116
352
  dependency_cache: DependencyCache,
117
353
  sandbox: SandboxBase,
@@ -142,13 +378,50 @@ async def test_run_overwrite_changed_file_with_storage_value(
142
378
 
143
379
  pathlib.Path('out.txt').write_text('42')
144
380
 
381
+ with pytest.raises(ValueError) as exc_info:
382
+ await configure_and_run_with_dest(pathlib.Path('out.txt'))
383
+ assert 'rbx clean' in str(exc_info.value)
384
+ assert (cleandir / 'out.txt').read_text().strip() == '42'
385
+
386
+
387
+ async def test_run_evicts_and_recreates_deleted_file_with_storage_value(
388
+ cleandir: pathlib.Path,
389
+ dependency_cache: DependencyCache,
390
+ sandbox: SandboxBase,
391
+ file_cacher: FileCacher,
392
+ ):
393
+ async def configure_and_run_with_dest(dest: pathlib.Path) -> GradingArtifacts:
394
+ executable = DigestOrSource.create(file_cacher.put_file_text('print(5)'))
395
+ artifacts = GradingArtifacts()
396
+ artifacts.inputs.append(
397
+ GradingFileInput(**executable.expand(), dest=pathlib.Path('executable.py'))
398
+ )
399
+ artifacts.outputs.append(
400
+ GradingFileOutput(src=pathlib.Path('box-out.txt'), dest=dest)
401
+ )
402
+ await steps_with_caching.run(
403
+ f'{sys.executable} executable.py',
404
+ params=SandboxParams(stdout_file=pathlib.Path('box-out.txt')),
405
+ sandbox=sandbox,
406
+ artifacts=artifacts,
407
+ dependency_cache=dependency_cache,
408
+ )
409
+ return artifacts
410
+
411
+ artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
412
+ assert (cleandir / 'out.txt').read_text().strip() == '5'
413
+ assert artifacts.logs is not None
414
+ assert not artifacts.logs.cached
415
+
416
+ pathlib.Path('out.txt').unlink()
417
+
145
418
  another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
146
419
  assert (cleandir / 'out.txt').read_text().strip() == '5'
147
420
  assert another_artifacts.logs is not None
148
421
  assert another_artifacts.logs.cached
149
422
 
150
423
 
151
- async def test_run_recreates_deleted_file_with_storage_value(
424
+ async def test_run_evicts_when_storage_value_deleted(
152
425
  cleandir: pathlib.Path,
153
426
  dependency_cache: DependencyCache,
154
427
  sandbox: SandboxBase,
@@ -177,12 +450,15 @@ async def test_run_recreates_deleted_file_with_storage_value(
177
450
  assert artifacts.logs is not None
178
451
  assert not artifacts.logs.cached
179
452
 
180
- pathlib.Path('out.txt').unlink()
453
+ # Delete the file from the cache
454
+ with pathlib.Path('out.txt').open('rb') as f:
455
+ digest = digest_cooperatively(f)
456
+ file_cacher.delete(digest)
181
457
 
182
458
  another_artifacts = await configure_and_run_with_dest(pathlib.Path('out.txt'))
183
459
  assert (cleandir / 'out.txt').read_text().strip() == '5'
184
460
  assert another_artifacts.logs is not None
185
- assert another_artifacts.logs.cached
461
+ assert not another_artifacts.logs.cached
186
462
 
187
463
 
188
464
  async def test_run_overwrite_exec_bit_when_changed(
@@ -14,6 +14,21 @@ command_substitutions:
14
14
  python2: python2
15
15
  python3: python3
16
16
 
17
+ caching:
18
+ # Set the caching level.
19
+ #
20
+ # CACHE_ALL: Cache everything.
21
+ # CACHE_COMPILATION: Cache only compilation.
22
+ # NO_CACHE: Do not cache anything.
23
+ level: CACHE_ALL
24
+
25
+ # Whether to check the integrity of the cached result, and evict it
26
+ # if file has changed since it was cached.
27
+ #
28
+ # Disable for more performance, but be careful to not modify any generated
29
+ # files.
30
+ check_integrity: true
31
+
17
32
  repeats:
18
33
  # Number of times to run the solution.
19
34
  reps: 1
@@ -14,6 +14,21 @@ command_substitutions:
14
14
  python2: python2
15
15
  python3: python3
16
16
 
17
+ caching:
18
+ # Set the caching level.
19
+ #
20
+ # CACHE_ALL: Cache everything.
21
+ # CACHE_COMPILATION: Cache only compilation.
22
+ # NO_CACHE: Do not cache anything.
23
+ level: CACHE_ALL
24
+
25
+ # Whether to check the integrity of the cached result, and evict it
26
+ # if file has changed since it was cached.
27
+ #
28
+ # Disable for more performance, but be careful to not modify any generated
29
+ # files.
30
+ check_integrity: true
31
+
17
32
  repeats:
18
33
  # Number of times to run the solution.
19
34
  reps: 1
@@ -0,0 +1,67 @@
1
+ ---
2
+ sandbox: "stupid"
3
+ defaultCompilation:
4
+ sandbox:
5
+ maxProcesses: 1000
6
+ timeLimit: 50000 # 50 seconds
7
+ wallTimeLimit: 50000 # 50 seconds
8
+ memoryLimit: 1024 # 1gb
9
+ defaultExecution:
10
+ sandbox:
11
+ # Useful for checkers, validators, etc.
12
+ timeLimit: 50000 # 50 seconds
13
+ wallTimeLimit: 50000 # 50 seconds
14
+ memoryLimit: 1024 # 1gb
15
+ languages:
16
+ - name: "cpp"
17
+ readableName: "C++20"
18
+ extension: "cpp"
19
+ compilation:
20
+ commands: ["g++ -std=c++20 -O2 -o {executable} {compilable}"]
21
+ execution:
22
+ command: "./{executable}"
23
+ fileMapping:
24
+ compilable: "compilable.cpp"
25
+ extensions:
26
+ boca:
27
+ bocaLanguage: "cc"
28
+ - name: "c"
29
+ readableName: "C"
30
+ extension: "c"
31
+ compilation:
32
+ commands: ["gcc -std=gnu11 -O2 -lm -o {executable} {compilable}"]
33
+ execution:
34
+ command: "./{executable}"
35
+ fileMapping:
36
+ compilable: "compilable.c"
37
+ - name: "py"
38
+ readableName: "Python3"
39
+ extension: "py"
40
+ execution:
41
+ command: "python3 {executable}"
42
+ fileMapping:
43
+ executable: "executable.py"
44
+ extensions:
45
+ boca:
46
+ bocaLanguage: "py3"
47
+ - name: "java"
48
+ readableName: "Java"
49
+ extension: "java"
50
+ compilation:
51
+ commands:
52
+ - "javac -Xlint -encoding UTF-8 {compilable}"
53
+ - "jar cvf {executable} @glob:*.class"
54
+ execution:
55
+ command:
56
+ "java -Xss100m -Xmx{{memory}}m -Xms{{initialMemory}}m -cp {executable}
57
+ Main"
58
+ fileMapping:
59
+ compilable: "Main.java"
60
+ executable: "Main.jar"
61
+ extensions:
62
+ boca:
63
+ languages: ["c", "cc", "java", "py3"]
64
+ flags:
65
+ c: "-O2 -lm -static"
66
+ cc: "-std=c++20 -O2 -lm -static"
67
+ preferContestLetter: true