rbx.cp 0.5.73__tar.gz → 0.6.1__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 (251) hide show
  1. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/PKG-INFO +23 -6
  2. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/README.md +19 -3
  3. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/pyproject.toml +13 -2
  4. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/annotations.py +21 -1
  5. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/cd.py +11 -1
  6. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/checkers.py +9 -1
  7. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/cli.py +59 -46
  8. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/code.py +142 -3
  9. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/build_contest_statements.py +44 -34
  10. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/contest_package.py +4 -7
  11. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/main.py +7 -58
  12. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/schema.py +52 -8
  13. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/statements.py +53 -25
  14. rbx_cp-0.6.1/rbx/box/creation.py +45 -0
  15. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/environment.py +21 -9
  16. rbx_cp-0.6.1/rbx/box/fields.py +35 -0
  17. rbx_cp-0.6.1/rbx/box/lang.py +27 -0
  18. rbx_cp-0.6.1/rbx/box/linting.py +26 -0
  19. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/package.py +4 -35
  20. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/boca/packager.py +48 -5
  21. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/contest_main.py +13 -0
  22. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/main.py +13 -2
  23. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/packager.py +4 -4
  24. rbx_cp-0.6.1/rbx/box/packaging/pkg/packager.py +142 -0
  25. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/polygon/packager.py +2 -24
  26. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/polygon/upload.py +35 -17
  27. rbx_cp-0.6.1/rbx/box/presets/__init__.py +718 -0
  28. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/presets/lock_schema.py +1 -2
  29. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/presets/schema.py +13 -5
  30. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/remote.py +2 -2
  31. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/retries.py +8 -0
  32. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/schema.py +82 -19
  33. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/solutions.py +77 -15
  34. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/build_statements.py +44 -27
  35. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/builders.py +18 -10
  36. rbx_cp-0.6.1/rbx/box/statements/expander.py +49 -0
  37. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/latex_jinja.py +61 -4
  38. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/schema.py +33 -9
  39. rbx_cp-0.6.1/rbx/box/stats.py +92 -0
  40. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/tasks.py +6 -3
  41. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/testcase_utils.py +19 -47
  42. rbx_cp-0.6.1/rbx/box/tooling/boca/main.py +13 -0
  43. rbx_cp-0.6.1/rbx/box/tooling/boca/scrape.py +34 -0
  44. rbx_cp-0.5.73/rbx/box/packaging/boca/upload.py → rbx_cp-0.6.1/rbx/box/tooling/boca/scraper.py +77 -8
  45. rbx_cp-0.6.1/rbx/box/tooling/main.py +8 -0
  46. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/utils/run_ui.py +1 -1
  47. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/interaction_box.py +19 -1
  48. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/caching.py +18 -2
  49. rbx_cp-0.6.1/rbx/grading/judge/__init__.py +0 -0
  50. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/sandbox.py +60 -5
  51. rbx_cp-0.6.1/rbx/grading/judge/sandboxes/__init__.py +0 -0
  52. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/sandboxes/isolate.py +1 -0
  53. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/sandboxes/stupid_sandbox.py +11 -5
  54. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/sandboxes/timeit.py +36 -15
  55. rbx_cp-0.6.1/rbx/grading/processing_context.py +71 -0
  56. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/steps.py +92 -40
  57. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/checker.sh +4 -1
  58. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/c +2 -6
  59. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/cc +2 -6
  60. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/cpp +2 -6
  61. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/java +1 -6
  62. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/kt +24 -28
  63. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/py2 +2 -6
  64. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compile/py3 +2 -6
  65. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactive/c +173 -0
  66. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactive/cc +173 -0
  67. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactive/cpp +173 -0
  68. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/interactive/java +15 -88
  69. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/interactive/kt +15 -88
  70. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactive/py2 +170 -0
  71. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactive/py3 +170 -0
  72. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/interactor_compile.sh +5 -2
  73. rbx_cp-0.6.1/rbx/resources/packagers/boca/interactor_run.sh +174 -0
  74. rbx_cp-0.6.1/rbx/resources/packagers/boca/safeexec.c +530 -0
  75. rbx_cp-0.6.1/rbx/resources/packagers/boca/safeexec_compile.sh +49 -0
  76. rbx_cp-0.6.1/rbx/resources/presets/default/contest/contest.rbx.yml +15 -0
  77. rbx_cp-0.6.1/rbx/resources/presets/default/problem/problem.rbx.yml +56 -0
  78. rbx_cp-0.6.1/rbx/resources/presets/default/problem/random.txt +4 -0
  79. rbx_cp-0.6.1/rbx/resources/presets/default/problem/rbx.h +92 -0
  80. rbx_cp-0.6.1/rbx/resources/presets/default/problem/statement/statement.rbx.tex +15 -0
  81. rbx_cp-0.6.1/rbx/resources/presets/default/problem/validator.cpp +16 -0
  82. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/templates/rbx.h +2 -3
  83. rbx_cp-0.5.73/rbx/box/creation.py +0 -78
  84. rbx_cp-0.5.73/rbx/box/presets/__init__.py +0 -637
  85. rbx_cp-0.5.73/rbx/grading/processing_context.py +0 -87
  86. rbx_cp-0.5.73/rbx/resources/packagers/boca/compile/pas +0 -172
  87. rbx_cp-0.5.73/rbx/resources/packagers/boca/interactive/c +0 -241
  88. rbx_cp-0.5.73/rbx/resources/packagers/boca/interactive/cc +0 -241
  89. rbx_cp-0.5.73/rbx/resources/packagers/boca/interactive/cpp +0 -241
  90. rbx_cp-0.5.73/rbx/resources/packagers/boca/interactive/py2 +0 -243
  91. rbx_cp-0.5.73/rbx/resources/packagers/boca/interactive/py3 +0 -243
  92. rbx_cp-0.5.73/rbx/resources/presets/default/contest/contest.rbx.yml +0 -14
  93. rbx_cp-0.5.73/rbx/resources/presets/default/problem/problem.rbx.yml +0 -44
  94. rbx_cp-0.5.73/rbx/resources/presets/default/problem/random.txt +0 -2
  95. rbx_cp-0.5.73/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  96. rbx_cp-0.5.73/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -18
  97. rbx_cp-0.5.73/rbx/resources/presets/default/problem/validator.cpp +0 -16
  98. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/LICENSE +0 -0
  99. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/__init__.py +0 -0
  100. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/autoenum.py +0 -0
  101. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/__init__.py +0 -0
  102. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/builder.py +0 -0
  103. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/compile.py +0 -0
  104. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/conftest.py +0 -0
  105. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/__init__.py +0 -0
  106. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/contest/contest_utils.py +0 -0
  107. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/deferred.py +0 -0
  108. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/download.py +0 -0
  109. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/dump_schemas.py +0 -0
  110. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/extensions.py +0 -0
  111. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/formatting.py +0 -0
  112. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/generators.py +0 -0
  113. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/generators_test.py +0 -0
  114. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/git_utils.py +0 -0
  115. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/header.py +0 -0
  116. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/lazy_importing_main.py +0 -0
  117. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/lazy_importing_test.py +0 -0
  118. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/main.py +0 -0
  119. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/naming.py +0 -0
  120. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/boca/extension.py +0 -0
  121. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/moj/packager.py +0 -0
  122. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  123. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/polygon/test.py +0 -0
  124. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  125. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/presets/fetch.py +0 -0
  126. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/sanitizers/warning_stack.py +0 -0
  127. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/setter_config.py +0 -0
  128. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/solutions_test.py +0 -0
  129. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/state.py +0 -0
  130. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/__init__.py +0 -0
  131. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/joiners.py +0 -0
  132. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/statements/latex.py +0 -0
  133. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/stresses.py +0 -0
  134. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/stressing/__init__.py +0 -0
  135. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/stressing/finder_parser.py +0 -0
  136. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/stressing/generator_parser.py +0 -0
  137. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/testcase_extractors.py +0 -0
  138. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/testcases/__init__.py +0 -0
  139. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/testcases/main.py +0 -0
  140. {rbx_cp-0.5.73/rbx/box/ui → rbx_cp-0.6.1/rbx/box/tooling}/__init__.py +0 -0
  141. {rbx_cp-0.5.73/rbx/box/ui/screens → rbx_cp-0.6.1/rbx/box/tooling/boca}/__init__.py +0 -0
  142. {rbx_cp-0.5.73/rbx/box/ui/utils → rbx_cp-0.6.1/rbx/box/ui}/__init__.py +0 -0
  143. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/captured_log.py +0 -0
  144. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/css/app.tcss +0 -0
  145. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/main.py +0 -0
  146. {rbx_cp-0.5.73/rbx/box/ui/widgets → rbx_cp-0.6.1/rbx/box/ui/screens}/__init__.py +0 -0
  147. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/build.py +0 -0
  148. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/command.py +0 -0
  149. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/differ.py +0 -0
  150. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/error.py +0 -0
  151. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  152. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/run.py +0 -0
  153. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/run_explorer.py +0 -0
  154. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  155. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/selector.py +0 -0
  156. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/screens/test_explorer.py +0 -0
  157. {rbx_cp-0.5.73/rbx/grading → rbx_cp-0.6.1/rbx/box/ui/utils}/__init__.py +0 -0
  158. {rbx_cp-0.5.73/rbx/grading/judge → rbx_cp-0.6.1/rbx/box/ui/widgets}/__init__.py +0 -0
  159. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/diff_box.py +0 -0
  160. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/file_log.py +0 -0
  161. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  162. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/test_output_box.py +0 -0
  163. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  164. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/unit.py +0 -0
  165. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/validators.py +0 -0
  166. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/box/validators_test.py +0 -0
  167. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/checker.py +0 -0
  168. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/clone.py +0 -0
  169. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/config.py +0 -0
  170. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/conftest.py +0 -0
  171. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/console.py +0 -0
  172. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/create.py +0 -0
  173. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/edit.py +0 -0
  174. {rbx_cp-0.5.73/rbx/grading/judge/sandboxes → rbx_cp-0.6.1/rbx/grading}/__init__.py +0 -0
  175. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/conftest.py +0 -0
  176. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/cacher.py +0 -0
  177. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/digester.py +0 -0
  178. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/storage.py +0 -0
  179. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/test.py +0 -0
  180. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/judge/testiso.py +0 -0
  181. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/limits.py +0 -0
  182. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/steps_with_caching.py +0 -0
  183. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading/steps_with_caching_run_test.py +0 -0
  184. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/grading_utils.py +0 -0
  185. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/hydration.py +0 -0
  186. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/main.py +0 -0
  187. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/metadata.py +0 -0
  188. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/providers/__init__.py +0 -0
  189. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/providers/codeforces.py +0 -0
  190. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/providers/provider.py +0 -0
  191. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/checkers/boilerplate.cpp +0 -0
  192. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/checkers/noop.cpp +0 -0
  193. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/default_config.json +0 -0
  194. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/default_setter_config.mac.yml +0 -0
  195. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/default_setter_config.yml +0 -0
  196. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/envs/default.rbx.yml +0 -0
  197. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/envs/isolate.rbx.yml +0 -0
  198. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/compare.sh +0 -0
  199. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/bkp +0 -0
  200. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/c +0 -0
  201. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/cc +0 -0
  202. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/cpp +0 -0
  203. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/java +0 -0
  204. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/kt +0 -0
  205. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/py2 +0 -0
  206. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/boca/run/py3 +0 -0
  207. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  208. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  209. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  210. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  211. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  212. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  213. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  214. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  215. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  216. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  217. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  218. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  219. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  220. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  221. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  222. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  223. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  224. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  225. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  226. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  227. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  228. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  229. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/.gitignore +0 -0
  230. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  231. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/random.py +0 -0
  232. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  233. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  234. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  235. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  236. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  237. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  238. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  239. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  240. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/resources/templates/template.cpp +0 -0
  241. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/run.py +0 -0
  242. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/schema.py +0 -0
  243. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/submit.py +0 -0
  244. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/submitors/__init__.py +0 -0
  245. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/submitors/codeforces.py +0 -0
  246. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/submitors/submitor.py +0 -0
  247. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/test.py +0 -0
  248. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/testcase.py +0 -0
  249. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/testcase_rendering.py +0 -0
  250. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/testing_utils.py +0 -0
  251. {rbx_cp-0.5.73 → rbx_cp-0.6.1}/rbx/utils.py +0 -0
@@ -1,11 +1,10 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.5.73
3
+ Version: 0.6.1
4
4
  Summary:
5
5
  Author: Roberto Sales
6
- Requires-Python: >=3.9,<4.0
6
+ Requires-Python: >=3.9.1,<4.0.0
7
7
  Classifier: Programming Language :: Python :: 3
8
- Classifier: Programming Language :: Python :: 3.9
9
8
  Classifier: Programming Language :: Python :: 3.10
10
9
  Classifier: Programming Language :: Python :: 3.11
11
10
  Classifier: Programming Language :: Python :: 3.12
@@ -16,6 +15,7 @@ Requires-Dist: beautifulsoup4 (>=4.13.4,<5.0.0)
16
15
  Requires-Dist: chardet (>=5.2.0,<6.0.0)
17
16
  Requires-Dist: colour (>=0.1.5,<0.2.0)
18
17
  Requires-Dist: dateparser (>=1.2.1,<2.0.0)
18
+ Requires-Dist: deepmerge (>=2.0,<3.0)
19
19
  Requires-Dist: fastapi (>=0.115.8,<0.116.0)
20
20
  Requires-Dist: filelock (>=3.14.0,<4.0.0)
21
21
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
@@ -39,10 +39,15 @@ Requires-Dist: syncer (>=2.0.3,<3.0.0)
39
39
  Requires-Dist: textual (>=3.1.1,<4.0.0)
40
40
  Requires-Dist: textual-serve (>=1.1.2,<2.0.0)
41
41
  Requires-Dist: typer (>=0.15.1,<0.16.0)
42
+ Requires-Dist: yamlfix (>=1.17.0,<2.0.0)
42
43
  Description-Content-Type: text/markdown
43
44
 
44
45
  <p align="center">
45
- <em>The go-to CLI tool for competitive programmers and setters.</em>
46
+ <img src="docs/rbx_white.png" alt="rbx" width="200">
47
+ </p>
48
+
49
+ <p align="center">
50
+ <em>The go-to CLI tool for programming competitions setters.</em>
46
51
  </p>
47
52
  <p align="center">
48
53
  <!-- loscal repository, no metadata badges. -->
@@ -59,6 +64,7 @@ Description-Content-Type: text/markdown
59
64
  <summary>Table of Contents</summary><br>
60
65
 
61
66
  - [Overview](#overview)
67
+ - [Features](#features)
62
68
  - [Documentation](#documentation)
63
69
  - [License](#license)
64
70
  </details>
@@ -72,9 +78,20 @@ Description-Content-Type: text/markdown
72
78
  [![PyPI pyversions](https://img.shields.io/pypi/pyversions/rbx.svg)](https://pypi.python.org/pypi/rbx/)
73
79
  [![PyPI version shields.io](https://img.shields.io/pypi/v/rbx.svg)](https://pypi.python.org/pypi/rbx/)
74
80
 
75
- rbx is a CLI tool and library that aims to help contestants and setters from the competitive programming community to have an easier time.
81
+ **rbx** is a CLI tool that empowers setters from the competitive programming community.
82
+
83
+ A flexible setting tool, as powerful as [Polygon](https://polygon.codeforces.com/), right on your terminal.
84
+
85
+ ---
86
+
87
+ ## Features
76
88
 
77
- Although trying to solve the issues of these two audiences seems to be too much for a single tool to handle, we exploit the fact that the journeys of contestants and setters have a lot in common.
89
+ - 🧱 Structure: describe your problem or contest structure with the use of YAML configuration files.
90
+ - 🤖 Generation: provides a simple way to describe your whole testset, including both manually added and generated testcases.
91
+ - 🔨 Testing: provides commands for automatically running correct and incorrect solutions against the testcases of your problem, automatically judging whether the verdict was as expected or not.
92
+ - ✅ Verify: checks if your testcases and solutions are strictly conformant with the use of validators and unit tests.
93
+ - 📝 Statements: provides tooling for writing and building statements, also ensuring they're easily synchronized with your testset.
94
+ - 📤 Package: provides a single command for packaging your problems for use in your preferred judge system.
78
95
 
79
96
  ---
80
97
 
@@ -1,5 +1,9 @@
1
1
  <p align="center">
2
- <em>The go-to CLI tool for competitive programmers and setters.</em>
2
+ <img src="docs/rbx_white.png" alt="rbx" width="200">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <em>The go-to CLI tool for programming competitions setters.</em>
3
7
  </p>
4
8
  <p align="center">
5
9
  <!-- loscal repository, no metadata badges. -->
@@ -16,6 +20,7 @@
16
20
  <summary>Table of Contents</summary><br>
17
21
 
18
22
  - [Overview](#overview)
23
+ - [Features](#features)
19
24
  - [Documentation](#documentation)
20
25
  - [License](#license)
21
26
  </details>
@@ -29,9 +34,20 @@
29
34
  [![PyPI pyversions](https://img.shields.io/pypi/pyversions/rbx.svg)](https://pypi.python.org/pypi/rbx/)
30
35
  [![PyPI version shields.io](https://img.shields.io/pypi/v/rbx.svg)](https://pypi.python.org/pypi/rbx/)
31
36
 
32
- rbx is a CLI tool and library that aims to help contestants and setters from the competitive programming community to have an easier time.
37
+ **rbx** is a CLI tool that empowers setters from the competitive programming community.
38
+
39
+ A flexible setting tool, as powerful as [Polygon](https://polygon.codeforces.com/), right on your terminal.
40
+
41
+ ---
42
+
43
+ ## Features
33
44
 
34
- Although trying to solve the issues of these two audiences seems to be too much for a single tool to handle, we exploit the fact that the journeys of contestants and setters have a lot in common.
45
+ - 🧱 Structure: describe your problem or contest structure with the use of YAML configuration files.
46
+ - 🤖 Generation: provides a simple way to describe your whole testset, including both manually added and generated testcases.
47
+ - 🔨 Testing: provides commands for automatically running correct and incorrect solutions against the testcases of your problem, automatically judging whether the verdict was as expected or not.
48
+ - ✅ Verify: checks if your testcases and solutions are strictly conformant with the use of validators and unit tests.
49
+ - 📝 Statements: provides tooling for writing and building statements, also ensuring they're easily synchronized with your testset.
50
+ - 📤 Package: provides a single command for packaging your problems for use in your preferred judge system.
35
51
 
36
52
  ---
37
53
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.5.73"
3
+ version = "0.6.1"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -12,7 +12,7 @@ exclude = [
12
12
  ]
13
13
 
14
14
  [tool.poetry.dependencies]
15
- python = "^3.9"
15
+ python = "^3.9.1"
16
16
  typer = "^0.15.1"
17
17
  fastapi = "^0.115.8"
18
18
  filelock = "^3.14.0"
@@ -42,6 +42,8 @@ aiofiles = "^24.1.0"
42
42
  colour = "^0.1.5"
43
43
  beautifulsoup4 = "^4.13.4"
44
44
  dateparser = "^1.2.1"
45
+ deepmerge = "^2.0"
46
+ yamlfix = "^1.17.0"
45
47
 
46
48
  [tool.poetry.scripts]
47
49
  rbc = "rbx.main:app"
@@ -62,7 +64,16 @@ pytest-cov = "^5.0.0"
62
64
  textual-dev = "^1.6.1"
63
65
  pytest-asyncio = "^0.26.0"
64
66
  mkdocs-gen-files = "^0.5.0"
67
+ markdown-grid-tables = "^0.5.0"
65
68
 
69
+
70
+ [tool.commitizen]
71
+ name = "cz_conventional_commits"
72
+ tag_format = "$version"
73
+ version_scheme = "pep440"
74
+ version_provider = "poetry"
75
+ update_changelog_on_bump = true
76
+ major_version_zero = true
66
77
  [build-system]
67
78
  requires = ["poetry-core"]
68
79
  build-backend = "poetry.core.masonry.api"
@@ -1,7 +1,7 @@
1
1
  import importlib.resources
2
2
  import pathlib
3
3
  import re
4
- from typing import List, Optional
4
+ from typing import Any, Dict, List, Optional
5
5
 
6
6
  import typer
7
7
  import typer.core
@@ -112,6 +112,26 @@ Checker = Annotated[
112
112
  ]
113
113
 
114
114
 
115
+ def parse_dictionary(value: Optional[str]) -> Dict[str, Any]:
116
+ if value is None:
117
+ return {}
118
+ res = {}
119
+ for item in value.split(','):
120
+ key, value = item.split('=', 1)
121
+ res[key] = value
122
+ return res
123
+
124
+
125
+ def parse_dictionary_items(items: Optional[List[str]]) -> Dict[str, Any]:
126
+ if items is None:
127
+ return {}
128
+ res = {}
129
+ for item in items:
130
+ key, value = item.split('=', 1)
131
+ res[key] = value
132
+ return res
133
+
134
+
115
135
  class AliasGroup(typer.core.TyperGroup):
116
136
  _CMD_SPLIT_P = re.compile(r', ?')
117
137
 
@@ -1,7 +1,7 @@
1
1
  import contextlib
2
2
  import functools
3
3
  import pathlib
4
- from typing import Optional
4
+ from typing import List, Optional
5
5
 
6
6
  import typer
7
7
 
@@ -25,6 +25,16 @@ def find_package(root: pathlib.Path = pathlib.Path()) -> Optional[pathlib.Path]:
25
25
  return root
26
26
 
27
27
 
28
+ def find_all_ancestor_packages(
29
+ root: pathlib.Path = pathlib.Path(),
30
+ ) -> List[pathlib.Path]:
31
+ packages = []
32
+ while (pkg := find_package(root)) is not None:
33
+ packages.append(pkg)
34
+ root = pkg.parent
35
+ return packages
36
+
37
+
28
38
  def is_problem_package(root: pathlib.Path = pathlib.Path()) -> bool:
29
39
  dir = find_package(root)
30
40
  if dir is None:
@@ -141,9 +141,16 @@ def _is_checker_exitcode(exitcode: int) -> bool:
141
141
  return exitcode in [0, 1, 2, 3]
142
142
 
143
143
 
144
+ def _get_last_line(message: str) -> str:
145
+ if not message:
146
+ return ''
147
+ return message.strip().split('\n')[-1]
148
+
149
+
144
150
  def process_checker_run_log(
145
151
  checker_run_log: Optional[RunLog], message: str
146
152
  ) -> CheckerResult:
153
+ message = _get_last_line(message)
147
154
  if (
148
155
  checker_run_log is not None
149
156
  and checker_run_log.exitstatus == SandboxBase.EXIT_SANDBOX_ERROR
@@ -352,7 +359,8 @@ async def check_communication(
352
359
  return _extra_check_and_sanitize(result)
353
360
 
354
361
  # Just a defensive pattern to ensure result is not None, should never happen.
355
- result = check_with_no_output(interactor_run_log)
362
+ if result is None:
363
+ result = check_with_no_output(interactor_run_log)
356
364
  if result.outcome != Outcome.ACCEPTED:
357
365
  if result.outcome == Outcome.RUNTIME_ERROR:
358
366
  result.outcome = Outcome.JUDGE_FAILED
@@ -26,7 +26,7 @@ from rbx.box import (
26
26
  )
27
27
  from rbx.box.contest import main as contest
28
28
  from rbx.box.contest.contest_package import find_contest_yaml
29
- from rbx.box.environment import VerificationLevel, get_environment_path
29
+ from rbx.box.environment import VerificationLevel, get_app_environment_path
30
30
  from rbx.box.header import generate_header
31
31
  from rbx.box.packaging import main as packaging
32
32
  from rbx.box.schema import CodeItem, ExpectedOutcome, TestcaseGroup
@@ -42,6 +42,7 @@ from rbx.box.solutions import (
42
42
  from rbx.box.statements import build_statements
43
43
  from rbx.box.testcase_utils import TestcaseEntry
44
44
  from rbx.box.testcases import main as testcases
45
+ from rbx.box.tooling import main as tooling
45
46
 
46
47
  app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
47
48
  app.add_typer(
@@ -93,6 +94,13 @@ app.add_typer(
93
94
  help='Manage testcases (sub-command).',
94
95
  rich_help_panel='Management',
95
96
  )
97
+ app.add_typer(
98
+ tooling.app,
99
+ name='tool, tooling',
100
+ cls=annotations.AliasGroup,
101
+ help='Manage tooling (sub-command).',
102
+ rich_help_panel='Misc',
103
+ )
96
104
 
97
105
 
98
106
  @app.callback()
@@ -106,7 +114,8 @@ def main(
106
114
  ),
107
115
  capture: bool = typer.Option(
108
116
  True,
109
- '--capture',
117
+ '--nocapture',
118
+ flag_value=False,
110
119
  help='Whether to save extra logs and outputs from interactive solutions.',
111
120
  ),
112
121
  ):
@@ -324,7 +333,7 @@ async def run(
324
333
  )
325
334
 
326
335
 
327
- async def _time_impl(check: bool, detailed: bool) -> Optional[int]:
336
+ async def _time_impl(check: bool, detailed: bool, runs: int = 0) -> Optional[int]:
328
337
  if package.get_main_solution() is None:
329
338
  console.console.print(
330
339
  '[warning]No main solution found, so cannot estimate a time limit.[/warning]'
@@ -344,6 +353,7 @@ async def _time_impl(check: bool, detailed: bool) -> Optional[int]:
344
353
  check=check,
345
354
  verification=VerificationLevel(verification),
346
355
  timelimit_override=-1, # Unlimited for time limit estimation
356
+ nruns=runs,
347
357
  )
348
358
 
349
359
  console.console.print()
@@ -388,6 +398,12 @@ async def time(
388
398
  '-d',
389
399
  help='Whether to print a detailed view of the tests using tables.',
390
400
  ),
401
+ runs: int = typer.Option(
402
+ 0,
403
+ '--runs',
404
+ '-r',
405
+ help='Number of runs to perform for each solution. Zero means the config default.',
406
+ ),
391
407
  ):
392
408
  main_solution = package.get_main_solution()
393
409
  if check and main_solution is None:
@@ -402,7 +418,7 @@ async def time(
402
418
  if not await builder.build(verification=verification, output=check):
403
419
  return None
404
420
 
405
- await _time_impl(check, detailed)
421
+ await _time_impl(check, detailed, runs)
406
422
 
407
423
 
408
424
  @app.command(
@@ -805,6 +821,7 @@ def header():
805
821
  generate_header()
806
822
 
807
823
 
824
+ # TODO: warn when using a preset (or show it)
808
825
  @app.command(
809
826
  'environment, env',
810
827
  rich_help_panel='Configuration',
@@ -822,15 +839,14 @@ def environment_command(
822
839
  ] = None,
823
840
  ):
824
841
  if env is None:
825
- cfg = config.get_config()
826
- console.console.print(f'Current environment: [item]{cfg.boxEnvironment}[/item]')
827
842
  console.console.print(
828
- f'Location: {environment.get_environment_path(cfg.boxEnvironment)}'
843
+ f'Current environment: [item]{environment.get_active_environment_description()}[/item]'
829
844
  )
845
+ console.console.print(f'Location: {environment.get_active_environment_path()}')
830
846
  return
831
847
  if install_from is not None:
832
848
  environment.install_environment(env, pathlib.Path(install_from))
833
- if not get_environment_path(env).is_file():
849
+ if not get_app_environment_path(env).is_file():
834
850
  console.console.print(
835
851
  f'[error]Environment [item]{env}[/item] does not exist.[/error]'
836
852
  )
@@ -843,7 +859,7 @@ def environment_command(
843
859
  )
844
860
  return
845
861
  console.console.print(
846
- f'Changing box environment from [item]{cfg.boxEnvironment}[/item] to [item]{env}[/item]...'
862
+ f'Changing global environment from [item]{cfg.boxEnvironment}[/item] to [item]{env}[/item]...'
847
863
  )
848
864
  cfg.boxEnvironment = env
849
865
  config.save_config(cfg)
@@ -852,43 +868,6 @@ def environment_command(
852
868
  clear()
853
869
 
854
870
 
855
- @app.command(
856
- 'activate',
857
- rich_help_panel='Configuration',
858
- help='Activate the environment of the current preset used by the package.',
859
- )
860
- @cd.within_closest_package
861
- def activate():
862
- preset_lock = presets.get_preset_lock()
863
- if preset_lock is None:
864
- console.console.print(
865
- '[warning]No configured preset to be activated for this package.[/warning]'
866
- )
867
- raise typer.Exit(1)
868
-
869
- preset = presets.get_installed_preset_or_null(preset_lock.preset_name)
870
- if preset is None:
871
- if preset_lock.uri is None:
872
- console.console.print(
873
- '[error]Preset is not installed. Install it manually, or specify a URI in [item].preset-lock.yml[/item].[/error]'
874
- )
875
- raise typer.Exit(1)
876
- presets.install(preset_lock.uri)
877
-
878
- preset = presets.get_installed_preset(preset_lock.preset_name)
879
-
880
- # Install the environment from the preset if it's not already installed.
881
- presets.optionally_install_environment_from_preset(
882
- preset, root=presets.get_preset_installation_path(preset_lock.name)
883
- )
884
-
885
- # Activate the environment.
886
- if preset.env is not None:
887
- environment_command(preset.name)
888
-
889
- console.console.print(f'[success]Preset [item]{preset.name}[/item] is activated.')
890
-
891
-
892
871
  @app.command(
893
872
  'languages',
894
873
  rich_help_panel='Configuration',
@@ -909,6 +888,40 @@ def languages():
909
888
  console.console.print()
910
889
 
911
890
 
891
+ @app.command(
892
+ 'stats',
893
+ rich_help_panel='Management',
894
+ help='Show stats about current and related packages.',
895
+ )
896
+ @cd.within_closest_package
897
+ def stats(
898
+ transitive: bool = typer.Option(
899
+ False,
900
+ '--transitive',
901
+ '-t',
902
+ help='Show stats about all reachable packages.',
903
+ ),
904
+ ):
905
+ from rbx.box import stats
906
+
907
+ if transitive:
908
+ stats.print_reachable_package_stats()
909
+ else:
910
+ stats.print_package_stats()
911
+
912
+
913
+ @app.command(
914
+ 'fix',
915
+ rich_help_panel='Management',
916
+ help='Format files of the current package.',
917
+ )
918
+ @cd.within_closest_package
919
+ def fix():
920
+ from rbx.box import linting
921
+
922
+ linting.fix_package()
923
+
924
+
912
925
  @app.command(
913
926
  'clear, clean',
914
927
  rich_help_panel='Management',
@@ -14,6 +14,7 @@ import typer
14
14
  from rbx import console
15
15
  from rbx.box import download, package, setter_config, state
16
16
  from rbx.box.environment import (
17
+ CompilationConfig,
17
18
  ExecutionConfig,
18
19
  FileMapping,
19
20
  get_compilation_config,
@@ -29,7 +30,7 @@ from rbx.box.formatting import get_formatted_memory
29
30
  from rbx.box.sanitizers import warning_stack
30
31
  from rbx.box.schema import CodeItem
31
32
  from rbx.grading import steps, steps_with_caching
32
- from rbx.grading.judge.sandbox import SandboxParams
33
+ from rbx.grading.judge.sandbox import SandboxBase, SandboxParams
33
34
  from rbx.grading.steps import (
34
35
  DigestHolder,
35
36
  DigestOrDest,
@@ -39,7 +40,10 @@ from rbx.grading.steps import (
39
40
  GradingFileOutput,
40
41
  RunLog,
41
42
  RunLogMetadata,
43
+ get_exe_from_command,
44
+ is_cpp_command,
42
45
  is_cxx_command,
46
+ maybe_get_bits_stdcpp_for_commands,
43
47
  )
44
48
 
45
49
 
@@ -391,12 +395,111 @@ def _prepare_run(
391
395
  )
392
396
 
393
397
 
398
+ def _should_precompile(commands: List[str]) -> bool:
399
+ return any(is_cpp_command(command) for command in commands)
400
+
401
+
402
+ def _precompile_header(
403
+ compilation_options: CompilationConfig,
404
+ sanitized: SanitizationLevel,
405
+ sandbox: SandboxBase,
406
+ sandbox_params: SandboxParams,
407
+ artifacts: GradingArtifacts,
408
+ input_artifact: GradingFileInput,
409
+ force_warnings: bool = False,
410
+ verbose: bool = False,
411
+ include_other_headers: bool = False,
412
+ ) -> GradingFileInput:
413
+ """
414
+ Precompile a header file (.h).
415
+
416
+ Assumes input artifact is a header file (.h) and compilation commands are C++.
417
+ """
418
+ assert compilation_options.commands is not None
419
+
420
+ dependency_cache = package.get_dependency_cache()
421
+
422
+ # TODO: deduplicate code with compile_item.
423
+ commands = get_mapped_commands(
424
+ compilation_options.commands,
425
+ FileMapping(
426
+ compilable='precompilable.h',
427
+ executable='precompilable.h.gch',
428
+ ),
429
+ )
430
+ commands = add_warning_flags(commands, force_warnings)
431
+ commands = substitute_commands(commands, sanitized=sanitized.should_sanitize())
432
+
433
+ if sanitized.should_sanitize():
434
+ commands = add_sanitizer_flags(commands)
435
+
436
+ precompilation_artifacts = GradingArtifacts()
437
+
438
+ # Keep only header files.
439
+ if include_other_headers:
440
+ precompilation_artifacts.inputs = [
441
+ input
442
+ for input in artifacts.inputs
443
+ if input.src is not None and input.src.suffix == '.h'
444
+ ]
445
+ precompilation_artifacts.inputs.append(
446
+ GradingFileInput(
447
+ src=input_artifact.src,
448
+ dest=PosixPath('precompilable.h'),
449
+ )
450
+ )
451
+
452
+ # Pull only the precompiled header file.
453
+ precompiled_digest = DigestHolder()
454
+ precompilation_artifacts.outputs.append(
455
+ GradingFileOutput(
456
+ src=PosixPath('precompilable.h.gch'),
457
+ digest=precompiled_digest,
458
+ executable=True,
459
+ )
460
+ )
461
+
462
+ if not steps_with_caching.compile(
463
+ commands,
464
+ params=sandbox_params,
465
+ artifacts=precompilation_artifacts,
466
+ sandbox=sandbox,
467
+ dependency_cache=dependency_cache,
468
+ ):
469
+ console.console.print(
470
+ f'[error]Failed to precompile header file: [item]{input_artifact.src}[/item][/error]'
471
+ )
472
+ raise typer.Exit(1)
473
+
474
+ if verbose:
475
+ console.console.print(
476
+ f'[status]Precompiled header file: [item]{input_artifact.src}[/item]'
477
+ )
478
+
479
+ if (
480
+ precompilation_artifacts.logs is not None
481
+ and precompilation_artifacts.logs.preprocess is not None
482
+ ):
483
+ for log in precompilation_artifacts.logs.preprocess:
484
+ console.console.print(f'[status]Command:[/status] {log.get_command()}')
485
+ console.console.print(f'[status]Summary:[/status] {log.get_summary()}')
486
+
487
+ assert precompiled_digest.value is not None
488
+
489
+ return GradingFileInput(
490
+ digest=precompiled_digest,
491
+ dest=input_artifact.dest.with_suffix('.h.gch'),
492
+ executable=True,
493
+ )
494
+
495
+
394
496
  # Compile code item and return its digest in the storage.
395
497
  def compile_item(
396
498
  code: CodeItem,
397
499
  sanitized: SanitizationLevel = SanitizationLevel.PREFER,
398
500
  force_warnings: bool = False,
399
501
  verbose: bool = False,
502
+ precompile: bool = True,
400
503
  ) -> str:
401
504
  _check_stack_limit()
402
505
 
@@ -461,6 +564,41 @@ def compile_item(
461
564
  for input in artifacts.inputs:
462
565
  _ignore_warning_in_cxx_input(input)
463
566
 
567
+ # Add system bits/stdc++.h to the compilation.
568
+ bits_artifact = maybe_get_bits_stdcpp_for_commands(commands)
569
+ if bits_artifact is not None:
570
+ artifacts.inputs.append(bits_artifact)
571
+ commands = [
572
+ command + ' -I.'
573
+ for command in commands
574
+ if is_cxx_command(get_exe_from_command(command))
575
+ ]
576
+
577
+ # Precompile C++ interesting header files.
578
+ if precompile and _should_precompile(commands):
579
+ precompilation_inputs = []
580
+ for input in artifacts.inputs:
581
+ if (
582
+ input.src is not None
583
+ and input.src.suffix == '.h'
584
+ and input.dest.name in ['stdc++.h', 'jngen.h', 'testlib.h']
585
+ ):
586
+ precompilation_inputs.append(
587
+ _precompile_header(
588
+ compilation_options,
589
+ sanitized,
590
+ sandbox,
591
+ sandbox_params,
592
+ artifacts,
593
+ input,
594
+ force_warnings,
595
+ verbose=False,
596
+ )
597
+ )
598
+ if precompilation_inputs:
599
+ artifacts.inputs.extend(precompilation_inputs)
600
+
601
+ # Compile the code.
464
602
  if not steps_with_caching.compile(
465
603
  commands,
466
604
  params=sandbox_params,
@@ -473,6 +611,7 @@ def compile_item(
473
611
  assert compiled_digest.value is not None
474
612
 
475
613
  if verbose and artifacts.logs is not None and artifacts.logs.preprocess is not None:
614
+ console.console.print(f'[status]Compiled item: [item]{code.path}[/item]')
476
615
  for log in artifacts.logs.preprocess:
477
616
  console.console.print(f'[status]Command:[/status] {log.get_command()}')
478
617
  console.console.print(f'[status]Summary:[/status] {log.get_summary()}')
@@ -589,8 +728,8 @@ async def run_communication(
589
728
  interactor_prepared.metadata.retryIndex = retry_index
590
729
  solution_prepared.metadata.retryIndex = retry_index
591
730
 
592
- interactor_prefix = 'INTERACTOR:'
593
- solution_prefix = 'SOLUTION:'
731
+ interactor_prefix = '<'
732
+ solution_prefix = '>'
594
733
 
595
734
  if merged_capture is not None:
596
735
  package.get_merged_capture_path().write_text(