rbx.cp 0.13.3__tar.gz → 0.13.4__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 (252) hide show
  1. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/PKG-INFO +2 -1
  2. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/pyproject.toml +3 -1
  3. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/annotations.py +5 -5
  4. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/checkers.py +24 -13
  5. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/cli.py +1 -4
  6. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/build_contest_statements.py +16 -3
  7. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/schema.py +1 -2
  8. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/fields.py +25 -1
  9. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/generators.py +5 -2
  10. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/global_package.py +5 -1
  11. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/header.py +19 -11
  12. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/package.py +3 -1
  13. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/presets/__init__.py +2 -2
  14. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/schema.py +4 -25
  15. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/build_statements.py +5 -1
  16. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/builders.py +7 -7
  17. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/schema.py +11 -2
  18. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/testcase_utils.py +2 -0
  19. rbx_cp-0.13.4/rbx/box/testing/testing_package.py +241 -0
  20. rbx_cp-0.13.4/rbx/box/testing/testing_preset.py +36 -0
  21. rbx_cp-0.13.4/rbx/box/testing/testing_shared.py +81 -0
  22. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/validators.py +2 -1
  23. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/caching.py +3 -2
  24. rbx_cp-0.13.4/rbx/grading/judge/sandboxes/__init__.py +0 -0
  25. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/shared/contest_template.rbx.tex +1 -1
  26. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/shared/problem_template.rbx.tex +5 -1
  27. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/testing_utils.py +1 -1
  28. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/utils.py +8 -0
  29. rbx_cp-0.13.3/rbx/box/conftest.py +0 -42
  30. rbx_cp-0.13.3/rbx/box/generators_test.py +0 -67
  31. rbx_cp-0.13.3/rbx/box/lazy_importing_test.py +0 -25
  32. rbx_cp-0.13.3/rbx/box/solutions_test.py +0 -47
  33. rbx_cp-0.13.3/rbx/box/validators_test.py +0 -15
  34. rbx_cp-0.13.3/rbx/checker.py +0 -128
  35. rbx_cp-0.13.3/rbx/clone.py +0 -197
  36. rbx_cp-0.13.3/rbx/conftest.py +0 -38
  37. rbx_cp-0.13.3/rbx/create.py +0 -37
  38. rbx_cp-0.13.3/rbx/edit.py +0 -24
  39. rbx_cp-0.13.3/rbx/grading/conftest.py +0 -33
  40. rbx_cp-0.13.3/rbx/grading/steps_with_caching_run_test.py +0 -707
  41. rbx_cp-0.13.3/rbx/grading_utils.py +0 -148
  42. rbx_cp-0.13.3/rbx/hydration.py +0 -101
  43. rbx_cp-0.13.3/rbx/main.py +0 -118
  44. rbx_cp-0.13.3/rbx/metadata.py +0 -105
  45. rbx_cp-0.13.3/rbx/run.py +0 -45
  46. rbx_cp-0.13.3/rbx/schema.py +0 -64
  47. rbx_cp-0.13.3/rbx/submit.py +0 -61
  48. rbx_cp-0.13.3/rbx/test.py +0 -349
  49. rbx_cp-0.13.3/rbx/testcase.py +0 -70
  50. rbx_cp-0.13.3/rbx/testcase_rendering.py +0 -79
  51. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/LICENSE +0 -0
  52. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/README.md +0 -0
  53. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/__init__.py +0 -0
  54. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/autoenum.py +0 -0
  55. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/__init__.py +0 -0
  56. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/builder.py +0 -0
  57. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/cd.py +0 -0
  58. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/code.py +0 -0
  59. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/compile.py +0 -0
  60. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/__init__.py +0 -0
  61. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/contest_package.py +0 -0
  62. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/contest_utils.py +0 -0
  63. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/main.py +0 -0
  64. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/contest/statements.py +0 -0
  65. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/creation.py +0 -0
  66. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/deferred.py +0 -0
  67. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/download.py +0 -0
  68. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/dump_schemas.py +0 -0
  69. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/environment.py +0 -0
  70. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/extensions.py +0 -0
  71. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/formatting.py +0 -0
  72. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/git_utils.py +0 -0
  73. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/lang.py +0 -0
  74. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/lazy_importing_main.py +0 -0
  75. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/linting.py +0 -0
  76. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/main.py +0 -0
  77. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/naming.py +0 -0
  78. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/__init__.py +0 -0
  79. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/boca/__init__.py +0 -0
  80. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/boca/extension.py +0 -0
  81. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/boca/packager.py +0 -0
  82. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/contest_main.py +0 -0
  83. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/importer.py +0 -0
  84. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/main.py +0 -0
  85. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/moj/packager.py +0 -0
  86. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/packager.py +0 -0
  87. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/pkg/packager.py +0 -0
  88. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/importer.py +0 -0
  89. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/packager.py +0 -0
  90. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  91. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/test.py +0 -0
  92. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/upload.py +0 -0
  93. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  94. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/presets/fetch.py +0 -0
  95. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/presets/lock_schema.py +0 -0
  96. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/presets/schema.py +0 -0
  97. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/remote.py +0 -0
  98. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/retries.py +0 -0
  99. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/sanitizers/warning_stack.py +0 -0
  100. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/setter_config.py +0 -0
  101. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/solutions.py +0 -0
  102. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/state.py +0 -0
  103. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/__init__.py +0 -0
  104. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/expander.py +0 -0
  105. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/joiners.py +0 -0
  106. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/latex.py +0 -0
  107. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/statements/latex_jinja.py +0 -0
  108. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/stats.py +0 -0
  109. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/stresses.py +0 -0
  110. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/stressing/__init__.py +0 -0
  111. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/stressing/finder_parser.py +0 -0
  112. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/stressing/generator_parser.py +0 -0
  113. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tasks.py +0 -0
  114. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/testcase_extractors.py +0 -0
  115. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/testcases/__init__.py +0 -0
  116. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/testcases/main.py +0 -0
  117. {rbx_cp-0.13.3/rbx/box/tooling → rbx_cp-0.13.4/rbx/box/testing}/__init__.py +0 -0
  118. {rbx_cp-0.13.3/rbx/box/tooling/boca → rbx_cp-0.13.4/rbx/box/tooling}/__init__.py +0 -0
  119. {rbx_cp-0.13.3/rbx/box/ui → rbx_cp-0.13.4/rbx/box/tooling/boca}/__init__.py +0 -0
  120. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tooling/boca/main.py +0 -0
  121. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tooling/boca/scrape.py +0 -0
  122. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tooling/boca/scraper.py +0 -0
  123. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tooling/converter.py +0 -0
  124. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/tooling/main.py +0 -0
  125. {rbx_cp-0.13.3/rbx/box/ui/screens → rbx_cp-0.13.4/rbx/box/ui}/__init__.py +0 -0
  126. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/captured_log.py +0 -0
  127. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/css/app.tcss +0 -0
  128. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/main.py +0 -0
  129. {rbx_cp-0.13.3/rbx/box/ui/utils → rbx_cp-0.13.4/rbx/box/ui/screens}/__init__.py +0 -0
  130. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/build.py +0 -0
  131. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/command.py +0 -0
  132. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/differ.py +0 -0
  133. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/error.py +0 -0
  134. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  135. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/run.py +0 -0
  136. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/run_explorer.py +0 -0
  137. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  138. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/selector.py +0 -0
  139. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/screens/test_explorer.py +0 -0
  140. {rbx_cp-0.13.3/rbx/box/ui/widgets → rbx_cp-0.13.4/rbx/box/ui/utils}/__init__.py +0 -0
  141. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/utils/run_ui.py +0 -0
  142. {rbx_cp-0.13.3/rbx/grading → rbx_cp-0.13.4/rbx/box/ui/widgets}/__init__.py +0 -0
  143. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/diff_box.py +0 -0
  144. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/file_log.py +0 -0
  145. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/interaction_box.py +0 -0
  146. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  147. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/test_output_box.py +0 -0
  148. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  149. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/box/unit.py +0 -0
  150. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/config.py +0 -0
  151. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/console.py +0 -0
  152. {rbx_cp-0.13.3/rbx/grading/judge → rbx_cp-0.13.4/rbx/grading}/__init__.py +0 -0
  153. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/debug_context.py +0 -0
  154. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/grading_context.py +0 -0
  155. {rbx_cp-0.13.3/rbx/grading/judge/sandboxes → rbx_cp-0.13.4/rbx/grading/judge}/__init__.py +0 -0
  156. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/cacher.py +0 -0
  157. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/digester.py +0 -0
  158. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/sandbox.py +0 -0
  159. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  160. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  161. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  162. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/storage.py +0 -0
  163. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/test.py +0 -0
  164. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/judge/testiso.py +0 -0
  165. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/limits.py +0 -0
  166. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/processing_context.py +0 -0
  167. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/profiling.py +0 -0
  168. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/steps.py +0 -0
  169. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/grading/steps_with_caching.py +0 -0
  170. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/providers/__init__.py +0 -0
  171. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/providers/codeforces.py +0 -0
  172. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/providers/provider.py +0 -0
  173. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/checkers/boilerplate.cpp +0 -0
  174. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/checkers/noop.cpp +0 -0
  175. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/default_config.json +0 -0
  176. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/default_setter_config.mac.yml +0 -0
  177. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/default_setter_config.yml +0 -0
  178. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/envs/default.rbx.yml +0 -0
  179. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/envs/isolate.rbx.yml +0 -0
  180. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/checker.sh +0 -0
  181. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compare.sh +0 -0
  182. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/c +0 -0
  183. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/cc +0 -0
  184. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/cpp +0 -0
  185. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/java +0 -0
  186. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/kt +0 -0
  187. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/py2 +0 -0
  188. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/compile/py3 +0 -0
  189. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/c +0 -0
  190. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/cc +0 -0
  191. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  192. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/java +0 -0
  193. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/kt +0 -0
  194. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  195. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  196. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  197. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/interactor_run.sh +0 -0
  198. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/bkp +0 -0
  199. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/c +0 -0
  200. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/cc +0 -0
  201. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/cpp +0 -0
  202. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/java +0 -0
  203. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/kt +0 -0
  204. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/py2 +0 -0
  205. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/run/py3 +0 -0
  206. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/safeexec.c +0 -0
  207. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/boca/safeexec_compile.sh +0 -0
  208. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  209. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  210. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  211. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  212. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  213. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  214. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  215. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  216. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  217. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  218. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  219. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  220. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  221. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  222. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  223. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  224. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  225. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  226. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/contest/.gitignore +0 -0
  227. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  228. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  229. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/contest/statement/instructions.tex +0 -0
  230. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/contest/statement/logo.png +0 -0
  231. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/env.rbx.yml +0 -0
  232. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  233. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/.gitignore +0 -0
  234. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/gens/gen.cpp +0 -0
  235. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/manual_tests/samples/000.in +0 -0
  236. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/manual_tests/samples/001.in +0 -0
  237. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  238. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/rbx.h +0 -0
  239. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  240. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  241. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  242. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  243. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/testplan/random.py +0 -0
  244. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/testplan/random.txt +0 -0
  245. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  246. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  247. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/presets/default/shared/icpc.sty +0 -0
  248. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/templates/rbx.h +0 -0
  249. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/resources/templates/template.cpp +0 -0
  250. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/submitors/__init__.py +0 -0
  251. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/submitors/codeforces.py +0 -0
  252. {rbx_cp-0.13.3 → rbx_cp-0.13.4}/rbx/submitors/submitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.13.3
3
+ Version: 0.13.4
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -40,6 +40,7 @@ Requires-Dist: requests (>=2.32.3,<3.0.0)
40
40
  Requires-Dist: rich (>=13.9.4,<14.0.0)
41
41
  Requires-Dist: ruamel-yaml (>=0.18.14,<0.19.0)
42
42
  Requires-Dist: ruyaml (>=0.91.0,<0.92.0)
43
+ Requires-Dist: sqlitedict (>=2.1.0,<3.0.0)
43
44
  Requires-Dist: syncer (>=2.0.3,<3.0.0)
44
45
  Requires-Dist: textual (>=3.1.1,<4.0.0)
45
46
  Requires-Dist: textual-serve (>=1.1.2,<2.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.13.3"
3
+ version = "0.13.4"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -49,6 +49,7 @@ ordered-set = "^4.1.0"
49
49
  gitignore-parser = "^0.1.12"
50
50
  pypandoc = "^1.15"
51
51
  iso639-lang = "^2.6.1"
52
+ sqlitedict = "^2.1.0"
52
53
 
53
54
  [tool.poetry.scripts]
54
55
  rbc = "rbx.main:app"
@@ -70,6 +71,7 @@ textual-dev = "^1.6.1"
70
71
  pytest-asyncio = "^0.26.0"
71
72
  mkdocs-gen-files = "^0.5.0"
72
73
  markdown-grid-tables = "^0.5.0"
74
+ pytest-xdist = "^3.8.0"
73
75
 
74
76
 
75
77
  [tool.commitizen]
@@ -7,7 +7,7 @@ import typer
7
7
  import typer.core
8
8
  from typing_extensions import Annotated
9
9
 
10
- from rbx import config, metadata
10
+ from rbx import config
11
11
  from rbx.config import get_config
12
12
 
13
13
 
@@ -21,10 +21,10 @@ def _get_language_default():
21
21
 
22
22
  def _get_problem_options():
23
23
  options = set()
24
- all_problems = metadata.find_problems()
25
- for problem in all_problems:
26
- options.add(problem.code)
27
- options.update(problem.aliases)
24
+ # all_problems = metadata.find_problems()
25
+ # for problem in all_problems:
26
+ # options.add(problem.code)
27
+ # options.update(problem.aliases)
28
28
  return sorted(options)
29
29
 
30
30
 
@@ -5,8 +5,7 @@ from typing import List, Optional
5
5
  import typer
6
6
 
7
7
  from rbx import console
8
- from rbx.box import package
9
- from rbx.box.code import SanitizationLevel, compile_item, run_item
8
+ from rbx.box import code, package
10
9
  from rbx.box.schema import Testcase
11
10
  from rbx.grading.judge.sandbox import SandboxBase
12
11
  from rbx.grading.steps import (
@@ -28,7 +27,7 @@ def compile_checker(progress: Optional[StatusProgress] = None) -> str:
28
27
  progress.update('Compiling checker...')
29
28
 
30
29
  try:
31
- digest = compile_item(checker, sanitized=SanitizationLevel.PREFER)
30
+ digest = code.compile_item(checker, sanitized=code.SanitizationLevel.PREFER)
32
31
  except Exception as e:
33
32
  console.console.print('[error]Failed compiling checker[/error]')
34
33
  raise typer.Exit(1) from e
@@ -46,7 +45,7 @@ def compile_interactor(progress: Optional[StatusProgress] = None) -> str:
46
45
  progress.update('Compiling interactor...')
47
46
 
48
47
  try:
49
- digest = compile_item(interactor, sanitized=SanitizationLevel.PREFER)
48
+ digest = code.compile_item(interactor, sanitized=code.SanitizationLevel.PREFER)
50
49
  except Exception as e:
51
50
  console.console.print('[error]Failed compiling interactor.[/error]')
52
51
  raise typer.Exit(1) from e
@@ -164,6 +163,14 @@ def process_checker_run_log(
164
163
 
165
164
  if checker_run_log is None:
166
165
  return CheckerResult(outcome=Outcome.INTERNAL_ERROR)
166
+ if checker_run_log.exitstatus not in [
167
+ SandboxBase.EXIT_OK,
168
+ SandboxBase.EXIT_NONZERO_RETURN,
169
+ ]:
170
+ return CheckerResult(
171
+ outcome=Outcome.JUDGE_FAILED,
172
+ message=f'checker failed with exit status {checker_run_log.exitstatus}: {message}',
173
+ )
167
174
  if not _is_checker_exitcode(checker_run_log.exitcode):
168
175
  return CheckerResult(
169
176
  outcome=Outcome.JUDGE_FAILED,
@@ -196,13 +203,17 @@ async def _check(
196
203
  if result.outcome != Outcome.ACCEPTED:
197
204
  return _convert_tle(result, run_log)
198
205
 
199
- pkg = package.find_problem_package_or_die()
200
- output_size = program_output.stat().st_size
201
- if output_size > pkg.outputLimit * 1024:
202
- return CheckerResult(
203
- outcome=Outcome.OUTPUT_LIMIT_EXCEEDED,
204
- message=f'Output size {pkg.outputLimit}kb, limit is {output_size // 1024}kb.',
205
- )
206
+ if (
207
+ run_log is not None
208
+ and run_log.metadata is not None
209
+ and run_log.metadata.limits.output is not None
210
+ ):
211
+ output_size = program_output.stat().st_size
212
+ if output_size > run_log.metadata.limits.output * 1024:
213
+ return CheckerResult(
214
+ outcome=Outcome.OUTPUT_LIMIT_EXCEEDED,
215
+ message=f'Output size {run_log.metadata.limits.output}kb, limit is {output_size // 1024}kb.',
216
+ )
206
217
 
207
218
  error = DigestHolder()
208
219
  inputs = [
@@ -219,14 +230,14 @@ async def _check(
219
230
  dest=pathlib.PosixPath('output.txt'),
220
231
  ),
221
232
  ]
222
- checker_run_log = await run_item(
233
+ checker_run_log = await code.run_item(
223
234
  package.get_checker(),
224
235
  DigestOrSource.create(checker_digest),
225
236
  stderr=DigestOrDest.create(error),
226
237
  inputs=inputs,
227
238
  extra_args='input.txt output.txt expected.txt',
228
239
  )
229
- message = package.get_digest_as_string(error.value or '') or ''
240
+ message = package.get_digest_as_string(error.value) or ''
230
241
 
231
242
  processed_checker_result = process_checker_run_log(checker_run_log, message)
232
243
  if processed_checker_result.outcome == Outcome.INTERNAL_ERROR:
@@ -128,7 +128,6 @@ def main(
128
128
  capture: bool = typer.Option(
129
129
  True,
130
130
  '--nocapture',
131
- flag_value=False,
132
131
  help='Whether to save extra logs and outputs from interactive solutions.',
133
132
  ),
134
133
  profile: bool = typer.Option(
@@ -259,7 +258,6 @@ async def run(
259
258
  check: bool = typer.Option(
260
259
  True,
261
260
  '--nocheck',
262
- flag_value=False,
263
261
  help='Whether to not build outputs for tests and run checker.',
264
262
  ),
265
263
  detailed: bool = typer.Option(
@@ -288,6 +286,7 @@ async def run(
288
286
  help='Whether to pick solutions interactively.',
289
287
  ),
290
288
  ):
289
+ console.console.log(check)
291
290
  main_solution = package.get_main_solution()
292
291
  if check and main_solution is None:
293
292
  console.console.print(
@@ -432,7 +431,6 @@ async def time(
432
431
  check: bool = typer.Option(
433
432
  True,
434
433
  '--nocheck',
435
- flag_value=False,
436
434
  help='Whether to not build outputs for tests and run checker.',
437
435
  ),
438
436
  detailed: bool = typer.Option(
@@ -488,7 +486,6 @@ async def irun(
488
486
  check: bool = typer.Option(
489
487
  True,
490
488
  '--nocheck',
491
- flag_value=False,
492
489
  help='Whether to not build outputs for tests and run checker.',
493
490
  ),
494
491
  generator: Optional[str] = typer.Option(
@@ -10,6 +10,7 @@ from rbx import console, testing_utils, utils
10
10
  from rbx.box import cd, package
11
11
  from rbx.box.contest.contest_package import get_problems
12
12
  from rbx.box.contest.schema import Contest, ContestProblem, ContestStatement
13
+ from rbx.box.fields import Primitive
13
14
  from rbx.box.formatting import href
14
15
  from rbx.box.schema import Package, Testcase
15
16
  from rbx.box.statements import build_statements, latex
@@ -72,14 +73,21 @@ def get_statement_builder_problems(
72
73
 
73
74
 
74
75
  def get_statement_builder_contest(
76
+ contest: Contest,
75
77
  statement: ContestStatement,
76
78
  extracted_problems: List[ExtractedProblem],
79
+ custom_vars: Optional[Dict[str, Primitive]] = None,
77
80
  ) -> StatementBuilderContest:
78
81
  return StatementBuilderContest(
79
82
  title=statement.title,
80
83
  location=statement.location,
81
84
  date=statement.date,
82
85
  problems=get_statement_builder_problems(extracted_problems),
86
+ vars={
87
+ **contest.expanded_vars,
88
+ **statement.expanded_vars,
89
+ **(custom_vars or {}),
90
+ },
83
91
  )
84
92
 
85
93
 
@@ -241,10 +249,13 @@ def build_contest_only(
241
249
  languages=get_environment_languages_for_statement(),
242
250
  params=params,
243
251
  root=pathlib.Path(td),
252
+ ),
253
+ item=get_statement_builder_contest(
254
+ contest,
255
+ statement,
256
+ extracted_problems,
244
257
  custom_vars=custom_vars,
245
- vars={**contest.expanded_vars, **statement.expanded_vars},
246
258
  ),
247
- item=get_statement_builder_contest(statement, extracted_problems),
248
259
  verbose=False,
249
260
  )
250
261
 
@@ -326,7 +337,9 @@ def build_statement_rooted(
326
337
  last_content = joiner.build(
327
338
  last_content,
328
339
  context=joiner_context,
329
- contest=get_statement_builder_contest(statement, extracted_problems),
340
+ contest=get_statement_builder_contest(
341
+ contest, statement, extracted_problems, custom_vars=custom_vars
342
+ ),
330
343
  )
331
344
  last_output = joiner.output_type()
332
345
 
@@ -3,8 +3,7 @@ from typing import Annotated, Dict, List, Optional
3
3
 
4
4
  from pydantic import AfterValidator, BaseModel, ConfigDict, Field, model_validator
5
5
 
6
- from rbx.box.fields import FNameField, NameField
7
- from rbx.box.schema import Primitive, expand_var
6
+ from rbx.box.fields import FNameField, NameField, Primitive, expand_var
8
7
  from rbx.box.statements.expander import expand_statements
9
8
  from rbx.box.statements.schema import (
10
9
  ConversionStep,
@@ -1,4 +1,4 @@
1
- from typing import TypeVar
1
+ from typing import Dict, TypeVar, Union
2
2
 
3
3
  from deepmerge import always_merger
4
4
  from pydantic import BaseModel, Field
@@ -33,3 +33,27 @@ def merge_pydantic_models(base: T, nxt: T) -> T:
33
33
  nxt_dict = nxt.model_dump(exclude_unset=True)
34
34
  merged_dict = always_merger.merge(base_dict, nxt_dict)
35
35
  return base.model_validate(merged_dict)
36
+
37
+
38
+ Primitive = Union[str, int, float, bool]
39
+
40
+
41
+ def expand_var(value: Primitive) -> Primitive:
42
+ if not isinstance(value, str):
43
+ return value
44
+ if value.startswith('\\'):
45
+ return value[1:]
46
+ if not value.startswith('py`') or not value.endswith('`'):
47
+ return value
48
+ res = eval(value[3:-1])
49
+ for supported_type in [str, int, float, bool]:
50
+ if isinstance(res, supported_type):
51
+ return res
52
+
53
+ raise TypeError(
54
+ f'Variable with backticks should evaluate to a primitive Python type: {value}'
55
+ )
56
+
57
+
58
+ def expand_vars(vars: Dict[str, Primitive]) -> Dict[str, Primitive]:
59
+ return {key: expand_var(value) for key, value in vars.items()}
@@ -63,13 +63,16 @@ def _warn_about_crlf(path: pathlib.Path):
63
63
 
64
64
 
65
65
  def _check_crlf(path: pathlib.Path):
66
+ should_fix = False
66
67
  with open(path, 'rb') as f:
67
68
  for line in f:
68
- if line.endswith(b'\r\n') or line.endswith(b'\n\r'):
69
+ if line.endswith(b'\r\n'):
69
70
  _warn_about_crlf(path)
71
+ should_fix = True
70
72
  break
71
73
 
72
- path.write_text(path.read_text().replace('\r', ''))
74
+ if should_fix:
75
+ path.write_text('\n'.join(path.read_text().splitlines()) + '\n')
73
76
 
74
77
 
75
78
  def _copy_testcase_over(
@@ -29,9 +29,13 @@ def is_cache_valid(cache_dir: pathlib.Path) -> bool:
29
29
  return True
30
30
 
31
31
 
32
+ def get_global_cache_dir_path() -> pathlib.Path:
33
+ return get_app_path() / '.box'
34
+
35
+
32
36
  @functools.cache
33
37
  def get_global_cache_dir() -> pathlib.Path:
34
- cache_dir = get_app_path() / '.box'
38
+ cache_dir = get_global_cache_dir_path()
35
39
  cache_dir.mkdir(parents=True, exist_ok=True)
36
40
  fingerprint_file = cache_dir / 'fingerprint'
37
41
  if not fingerprint_file.is_file():
@@ -1,11 +1,12 @@
1
1
  import functools
2
2
  import importlib
3
3
  import importlib.resources
4
+ import json
4
5
  import pathlib
5
6
  from typing import Callable, Dict, Type
6
7
 
7
8
  from rbx.box import package
8
- from rbx.box.schema import Primitive
9
+ from rbx.box.fields import Primitive
9
10
 
10
11
 
11
12
  @functools.cache
@@ -40,12 +41,21 @@ def _preprocess_header(header: str) -> str:
40
41
  )
41
42
 
42
43
 
44
+ def _string_repr(s):
45
+ return json.dumps(s)
46
+
47
+
43
48
  def _get_string_var_block() -> str:
44
- return _get_var_block(_get_vars_of_type(str, lambda x: f'{x:!r}'))
49
+ return _get_var_block(_get_vars_of_type(str, _string_repr))
45
50
 
46
51
 
47
52
  def _get_int_var_block() -> str:
48
- return _get_var_block(_get_vars_of_type(int, lambda x: str(x)))
53
+ def _transform(x: Primitive) -> str:
54
+ if isinstance(x, bool):
55
+ return str(int(x))
56
+ return str(x)
57
+
58
+ return _get_var_block(_get_vars_of_type(int, _transform))
49
59
 
50
60
 
51
61
  def _get_float_var_block() -> str:
@@ -56,16 +66,14 @@ def _get_bool_var_block() -> str:
56
66
  return _get_var_block(_get_vars_of_type(bool, lambda x: 'true' if x else 'false'))
57
67
 
58
68
 
59
- def _get_vars_of_type(
60
- type: Type, transform: Callable[[Primitive], str]
61
- ) -> Dict[str, str]:
69
+ def _get_vars_of_type(t: Type, transform: Callable[[Primitive], str]) -> Dict[str, str]:
62
70
  pkg = package.find_problem_package_or_die()
63
71
  vars = pkg.expanded_vars
64
- return {
65
- name: transform(value)
66
- for name, value in vars.items()
67
- if isinstance(value, type)
68
- }
72
+
73
+ def is_valid(value: Primitive) -> bool:
74
+ return isinstance(value, t)
75
+
76
+ return {name: transform(value) for name, value in vars.items() if is_valid(value)}
69
77
 
70
78
 
71
79
  def _get_var_block(mappings: Dict[str, str]) -> str:
@@ -181,8 +181,10 @@ def get_file_cacher(root: pathlib.Path = pathlib.Path()) -> FileCacher:
181
181
 
182
182
  @functools.cache
183
183
  def get_digest_as_string(
184
- digest: str, root: pathlib.Path = pathlib.Path()
184
+ digest: Optional[str], root: pathlib.Path = pathlib.Path()
185
185
  ) -> Optional[str]:
186
+ if not digest:
187
+ return None
186
188
  cacher = get_file_cacher(root)
187
189
  try:
188
190
  content = cacher.get_file_content(digest)
@@ -339,9 +339,9 @@ def _copy_preset_file(
339
339
 
340
340
  # The symlink points somewhere inside the preset folder, fix the symlink.
341
341
  dst_absolute_path = utils.abspath(dst)
342
- fixed_target_relative_path = target_absolute_path.relative_to(
342
+ fixed_target_relative_path = utils.relpath(
343
+ target_absolute_path,
343
344
  dst_absolute_path.parent,
344
- walk_up=True,
345
345
  )
346
346
  dst.symlink_to(fixed_target_relative_path)
347
347
 
@@ -3,19 +3,17 @@ from __future__ import annotations
3
3
  import os
4
4
  import pathlib
5
5
  import re
6
- from typing import Annotated, Any, Dict, List, Optional, Union
6
+ from typing import Annotated, Any, Dict, List, Optional
7
7
 
8
8
  from pydantic import AfterValidator, BaseModel, ConfigDict, Field, model_validator
9
9
  from pydantic_core import PydanticCustomError
10
10
 
11
11
  from rbx.autoenum import AutoEnum, alias
12
- from rbx.box.fields import NameField
12
+ from rbx.box.fields import NameField, Primitive, expand_vars
13
13
  from rbx.box.statements.expander import expand_statements
14
14
  from rbx.box.statements.schema import Statement
15
15
  from rbx.grading.steps import Outcome
16
16
 
17
- Primitive = Union[str, int, float, bool]
18
-
19
17
 
20
18
  def _check_oneof(model_obj: BaseModel, fields: List[str]):
21
19
  has = []
@@ -30,27 +28,6 @@ def _check_oneof(model_obj: BaseModel, fields: List[str]):
30
28
  )
31
29
 
32
30
 
33
- def expand_var(value: Primitive) -> Primitive:
34
- if not isinstance(value, str):
35
- return value
36
- if value.startswith('\\'):
37
- return value[1:]
38
- if not value.startswith('py`') or not value.endswith('`'):
39
- return value
40
- res = eval(value[3:-1])
41
- for supported_type in [str, int, float, bool]:
42
- if isinstance(res, supported_type):
43
- return res
44
-
45
- raise TypeError(
46
- f'Variable with backticks should evaluate to a primitive Python type: {value}'
47
- )
48
-
49
-
50
- def expand_vars(vars: Dict[str, Primitive]) -> Dict[str, Primitive]:
51
- return {key: expand_var(value) for key, value in vars.items()}
52
-
53
-
54
31
  def _represents_int(s: str) -> bool:
55
32
  return re.match(r'[-+]?\d+$', s.strip()) is not None
56
33
 
@@ -245,6 +222,8 @@ Whether this interactor is a legacy interactor and needs a checker to be specifi
245
222
 
246
223
 
247
224
  class Testcase(BaseModel):
225
+ __test__ = False
226
+
248
227
  model_config = ConfigDict(extra='forbid')
249
228
 
250
229
  inputPath: pathlib.Path = Field(description="""The path of the input file.""")
@@ -262,7 +262,6 @@ def build_statement_bytes(
262
262
  languages=get_environment_languages_for_statement(),
263
263
  params=params,
264
264
  root=pathlib.Path(td),
265
- custom_vars=custom_vars,
266
265
  ),
267
266
  item=StatementBuilderProblem(
268
267
  package=pkg,
@@ -271,6 +270,11 @@ def build_statement_bytes(
271
270
  get_samples() if use_samples else []
272
271
  ),
273
272
  short_name=short_name,
273
+ vars={
274
+ **pkg.expanded_vars,
275
+ **statement.expanded_vars,
276
+ **(custom_vars or {}),
277
+ },
274
278
  ),
275
279
  verbose=False,
276
280
  )
@@ -11,7 +11,8 @@ import typer
11
11
  from pydantic import BaseModel
12
12
 
13
13
  from rbx import console, utils
14
- from rbx.box.schema import Package, Primitive, Testcase
14
+ from rbx.box.fields import Primitive
15
+ from rbx.box.schema import Package, Testcase
15
16
  from rbx.box.statements.latex_jinja import (
16
17
  JinjaDictWrapper,
17
18
  render_latex_template,
@@ -48,8 +49,6 @@ class StatementBuilderContext:
48
49
  languages: List[StatementCodeLanguage]
49
50
  params: ConversionStep
50
51
  root: pathlib.Path
51
- custom_vars: Optional[Dict[str, Any]] = None
52
- vars: Optional[Dict[str, Primitive]] = None
53
52
 
54
53
  def build_jinja_kwargs(self) -> Dict[str, Any]:
55
54
  res = {
@@ -57,9 +56,6 @@ class StatementBuilderContext:
57
56
  'languages': self.languages,
58
57
  'keyed_languages': {lang.id: lang for lang in self.languages},
59
58
  }
60
- if self.vars is not None or self.custom_vars is not None:
61
- res['vars'] = self.vars or {}
62
- res['vars'].update(self.custom_vars or {})
63
59
  return res
64
60
 
65
61
 
@@ -125,12 +121,14 @@ class StatementBuilderProblem(StatementBuilderItem):
125
121
  # Will only be filled by contests.
126
122
  io_path: Optional[pathlib.Path] = None
127
123
 
124
+ vars: Optional[Dict[str, Primitive]] = None
125
+
128
126
  def build_inner_jinja_kwargs(self) -> Dict[str, Any]:
129
127
  kwargs = {
130
128
  'package': self.package,
131
129
  'statement': self.statement,
132
130
  'samples': self.samples,
133
- 'vars': JinjaDictWrapper(self.package.expanded_vars, key='vars'),
131
+ 'vars': JinjaDictWrapper(self.vars or {}, key='vars'),
134
132
  'title': self.statement.title or self.package.name,
135
133
  }
136
134
  if self.short_name is not None:
@@ -152,6 +150,7 @@ class StatementBuilderContest(StatementBuilderItem):
152
150
  location: Optional[str] = None
153
151
  date: Optional[str] = None
154
152
  problems: List[StatementBuilderProblem] = dataclasses.field(default_factory=list)
153
+ vars: Optional[Dict[str, Primitive]] = None
155
154
 
156
155
  def build_inner_jinja_kwargs(self) -> Dict[str, Any]:
157
156
  res = {'title': self.title}
@@ -167,6 +166,7 @@ class StatementBuilderContest(StatementBuilderItem):
167
166
  'problems': [
168
167
  problem.build_inner_jinja_kwargs() for problem in self.problems
169
168
  ],
169
+ 'vars': JinjaDictWrapper(self.vars or {}, key='vars'),
170
170
  }
171
171
  return res
172
172
 
@@ -2,12 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import pathlib
4
4
  from enum import Enum
5
- from typing import Annotated, List, Literal, Optional, Union
5
+ from typing import Annotated, Dict, List, Literal, Optional, Union
6
6
 
7
7
  from pydantic import AfterValidator, BaseModel, ConfigDict, Field
8
8
 
9
9
  from rbx.autoenum import AutoEnum, alias
10
- from rbx.box.fields import FNameField
10
+ from rbx.box.fields import FNameField, Primitive, expand_var
11
11
  from rbx.box.lang import is_valid_lang_code
12
12
 
13
13
 
@@ -175,3 +175,12 @@ the statement. Files will be included in the same folder as the statement file,
175
175
  their relativeness. Can be glob pattern as well, such as `imgs/*.png`.
176
176
  """,
177
177
  )
178
+
179
+ vars: Dict[str, Primitive] = Field(
180
+ default={},
181
+ description='Variables to be used in the statement.',
182
+ )
183
+
184
+ @property
185
+ def expanded_vars(self) -> Dict[str, Primitive]:
186
+ return {key: expand_var(value) for key, value in self.vars.items()}
@@ -14,6 +14,8 @@ from rbx.box.schema import Testcase, TestcaseGroup
14
14
 
15
15
 
16
16
  class TestcaseEntry(BaseModel):
17
+ __test__ = False
18
+
17
19
  group: str
18
20
  index: int
19
21