rbx.cp 0.11.2__tar.gz → 0.12.0__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 (247) hide show
  1. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/PKG-INFO +1 -1
  2. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/pyproject.toml +1 -1
  3. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/builder.py +3 -3
  4. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/build_contest_statements.py +5 -6
  5. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/statements.py +0 -1
  6. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/generators.py +54 -12
  7. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/package.py +56 -4
  8. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/polygon/upload.py +2 -3
  9. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/schema.py +3 -3
  10. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/solutions.py +8 -12
  11. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/build_statements.py +0 -1
  12. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/latex.py +11 -0
  13. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/caching.py +1 -0
  14. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/sandbox.py +1 -0
  15. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/steps.py +1 -0
  16. rbx_cp-0.12.0/rbx/resources/presets/default/contest/.gitignore +21 -0
  17. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/contest/contest.rbx.yml +2 -2
  18. rbx_cp-0.12.0/rbx/resources/presets/default/problem/.gitignore +21 -0
  19. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/validator.cpp +2 -1
  20. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/shared/icpc.sty +1 -1
  21. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/utils.py +13 -0
  22. rbx_cp-0.11.2/rbx/resources/presets/default/contest/.gitignore +0 -6
  23. rbx_cp-0.11.2/rbx/resources/presets/default/problem/.gitignore +0 -6
  24. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/LICENSE +0 -0
  25. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/README.md +0 -0
  26. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/__init__.py +0 -0
  27. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/annotations.py +0 -0
  28. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/autoenum.py +0 -0
  29. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/__init__.py +0 -0
  30. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/cd.py +0 -0
  31. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/checkers.py +0 -0
  32. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/cli.py +0 -0
  33. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/code.py +0 -0
  34. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/compile.py +0 -0
  35. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/conftest.py +0 -0
  36. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/__init__.py +0 -0
  37. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/contest_package.py +0 -0
  38. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/contest_utils.py +0 -0
  39. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/main.py +0 -0
  40. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/contest/schema.py +0 -0
  41. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/creation.py +0 -0
  42. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/deferred.py +0 -0
  43. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/download.py +0 -0
  44. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/dump_schemas.py +0 -0
  45. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/environment.py +0 -0
  46. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/extensions.py +0 -0
  47. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/fields.py +0 -0
  48. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/formatting.py +0 -0
  49. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/generators_test.py +0 -0
  50. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/git_utils.py +0 -0
  51. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/global_package.py +0 -0
  52. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/header.py +0 -0
  53. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/lang.py +0 -0
  54. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/lazy_importing_main.py +0 -0
  55. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/lazy_importing_test.py +0 -0
  56. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/linting.py +0 -0
  57. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/main.py +0 -0
  58. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/naming.py +0 -0
  59. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/__init__.py +0 -0
  60. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/boca/__init__.py +0 -0
  61. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/boca/extension.py +0 -0
  62. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/boca/packager.py +0 -0
  63. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/contest_main.py +0 -0
  64. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/main.py +0 -0
  65. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/moj/packager.py +0 -0
  66. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/packager.py +0 -0
  67. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/pkg/packager.py +0 -0
  68. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/polygon/packager.py +0 -0
  69. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  70. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/polygon/test.py +0 -0
  71. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  72. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/presets/__init__.py +0 -0
  73. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/presets/fetch.py +0 -0
  74. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/presets/lock_schema.py +0 -0
  75. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/presets/schema.py +0 -0
  76. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/remote.py +0 -0
  77. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/retries.py +0 -0
  78. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/sanitizers/warning_stack.py +0 -0
  79. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/setter_config.py +0 -0
  80. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/solutions_test.py +0 -0
  81. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/state.py +0 -0
  82. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/__init__.py +0 -0
  83. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/builders.py +0 -0
  84. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/expander.py +0 -0
  85. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/joiners.py +0 -0
  86. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/latex_jinja.py +0 -0
  87. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/statements/schema.py +0 -0
  88. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/stats.py +0 -0
  89. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/stresses.py +0 -0
  90. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/stressing/__init__.py +0 -0
  91. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/stressing/finder_parser.py +0 -0
  92. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/stressing/generator_parser.py +0 -0
  93. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tasks.py +0 -0
  94. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/testcase_extractors.py +0 -0
  95. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/testcase_utils.py +0 -0
  96. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/testcases/__init__.py +0 -0
  97. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/testcases/main.py +0 -0
  98. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/__init__.py +0 -0
  99. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/boca/__init__.py +0 -0
  100. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/boca/main.py +0 -0
  101. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/boca/scrape.py +0 -0
  102. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/boca/scraper.py +0 -0
  103. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/tooling/main.py +0 -0
  104. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/__init__.py +0 -0
  105. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/captured_log.py +0 -0
  106. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/css/app.tcss +0 -0
  107. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/main.py +0 -0
  108. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/__init__.py +0 -0
  109. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/build.py +0 -0
  110. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/command.py +0 -0
  111. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/differ.py +0 -0
  112. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/error.py +0 -0
  113. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  114. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/run.py +0 -0
  115. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/run_explorer.py +0 -0
  116. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  117. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/selector.py +0 -0
  118. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/screens/test_explorer.py +0 -0
  119. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/utils/__init__.py +0 -0
  120. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/utils/run_ui.py +0 -0
  121. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/__init__.py +0 -0
  122. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/diff_box.py +0 -0
  123. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/file_log.py +0 -0
  124. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/interaction_box.py +0 -0
  125. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  126. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/test_output_box.py +0 -0
  127. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  128. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/unit.py +0 -0
  129. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/validators.py +0 -0
  130. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/box/validators_test.py +0 -0
  131. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/checker.py +0 -0
  132. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/clone.py +0 -0
  133. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/config.py +0 -0
  134. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/conftest.py +0 -0
  135. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/console.py +0 -0
  136. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/create.py +0 -0
  137. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/edit.py +0 -0
  138. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/__init__.py +0 -0
  139. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/conftest.py +0 -0
  140. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/debug_context.py +0 -0
  141. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/grading_context.py +0 -0
  142. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/__init__.py +0 -0
  143. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/cacher.py +0 -0
  144. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/digester.py +0 -0
  145. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  146. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  147. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  148. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  149. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/storage.py +0 -0
  150. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/test.py +0 -0
  151. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/judge/testiso.py +0 -0
  152. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/limits.py +0 -0
  153. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/processing_context.py +0 -0
  154. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/profiling.py +0 -0
  155. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/steps_with_caching.py +0 -0
  156. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading/steps_with_caching_run_test.py +0 -0
  157. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/grading_utils.py +0 -0
  158. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/hydration.py +0 -0
  159. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/main.py +0 -0
  160. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/metadata.py +0 -0
  161. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/providers/__init__.py +0 -0
  162. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/providers/codeforces.py +0 -0
  163. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/providers/provider.py +0 -0
  164. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/checkers/boilerplate.cpp +0 -0
  165. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/checkers/noop.cpp +0 -0
  166. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/default_config.json +0 -0
  167. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/default_setter_config.mac.yml +0 -0
  168. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/default_setter_config.yml +0 -0
  169. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/envs/default.rbx.yml +0 -0
  170. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/envs/isolate.rbx.yml +0 -0
  171. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/checker.sh +0 -0
  172. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compare.sh +0 -0
  173. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/c +0 -0
  174. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/cc +0 -0
  175. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/cpp +0 -0
  176. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/java +0 -0
  177. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/kt +0 -0
  178. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/py2 +0 -0
  179. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/compile/py3 +0 -0
  180. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/c +0 -0
  181. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/cc +0 -0
  182. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  183. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/java +0 -0
  184. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/kt +0 -0
  185. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  186. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  187. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  188. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/interactor_run.sh +0 -0
  189. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/bkp +0 -0
  190. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/c +0 -0
  191. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/cc +0 -0
  192. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/cpp +0 -0
  193. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/java +0 -0
  194. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/kt +0 -0
  195. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/py2 +0 -0
  196. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/run/py3 +0 -0
  197. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/safeexec.c +0 -0
  198. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/boca/safeexec_compile.sh +0 -0
  199. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  200. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  201. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  202. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  203. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  204. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  205. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  206. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  207. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  208. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  209. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  210. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  211. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  212. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  213. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  214. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  215. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  216. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  217. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  218. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/contest/statement/instructions.tex +0 -0
  219. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/contest/statement/logo.png +0 -0
  220. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/env.rbx.yml +0 -0
  221. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  222. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/gens/gen.cpp +0 -0
  223. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/manual_tests/samples/000.in +0 -0
  224. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/manual_tests/samples/001.in +0 -0
  225. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  226. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/rbx.h +0 -0
  227. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  228. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  229. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  230. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  231. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/testplan/random.py +0 -0
  232. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/testplan/random.txt +0 -0
  233. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  234. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/shared/contest_template.rbx.tex +0 -0
  235. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/presets/default/shared/problem_template.rbx.tex +0 -0
  236. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/templates/rbx.h +0 -0
  237. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/resources/templates/template.cpp +0 -0
  238. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/run.py +0 -0
  239. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/schema.py +0 -0
  240. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/submit.py +0 -0
  241. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/submitors/__init__.py +0 -0
  242. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/submitors/codeforces.py +0 -0
  243. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/submitors/submitor.py +0 -0
  244. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/test.py +0 -0
  245. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/testcase.py +0 -0
  246. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/testcase_rendering.py +0 -0
  247. {rbx_cp-0.11.2 → rbx_cp-0.12.0}/rbx/testing_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.11.2
3
+ Version: 0.12.0
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.11.2"
3
+ version = "0.12.0"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -96,10 +96,10 @@ async def verify(verification: environment.VerificationParam) -> bool:
96
96
 
97
97
  tracked_solutions = None
98
98
  if verification < VerificationLevel.ALL_SOLUTIONS.value:
99
- pkg = package.find_problem_package_or_die()
100
-
101
99
  tracked_solutions = {
102
- str(solution.path) for solution in pkg.solutions if is_fast(solution)
100
+ str(solution.path)
101
+ for solution in package.get_solutions()
102
+ if is_fast(solution)
103
103
  }
104
104
 
105
105
  with utils.StatusProgress('Running solutions...') as s:
@@ -1,6 +1,5 @@
1
1
  import dataclasses
2
2
  import pathlib
3
- import subprocess
4
3
  import tempfile
5
4
  import typing
6
5
  from typing import Any, Dict, List, Optional, Tuple
@@ -13,7 +12,7 @@ from rbx.box.contest.contest_package import get_problems
13
12
  from rbx.box.contest.schema import Contest, ContestProblem, ContestStatement
14
13
  from rbx.box.formatting import href
15
14
  from rbx.box.schema import Package, Testcase
16
- from rbx.box.statements import build_statements
15
+ from rbx.box.statements import build_statements, latex
17
16
  from rbx.box.statements.build_statements import (
18
17
  get_builders,
19
18
  get_environment_languages_for_statement,
@@ -253,10 +252,10 @@ def build_contest_only(
253
252
  console.console.log(
254
253
  f'Installing LaTeX packages for [item]{statement.name} {statement.language}[/item]...'
255
254
  )
256
- subprocess.run(
257
- ['texliveonfly', output],
258
- cwd=td,
259
- )
255
+ tmp_file = pathlib.Path(td) / '__tmp_install__.tex'
256
+ tmp_file.write_bytes(output)
257
+ latex.install_tex_packages(tmp_file, pathlib.Path(td))
258
+
260
259
  last_content = output
261
260
  last_output = bdr.output_type()
262
261
 
@@ -49,7 +49,6 @@ async def build(
49
49
  vars: Annotated[
50
50
  Optional[List[str]],
51
51
  typer.Option(
52
- '-v',
53
52
  '--vars',
54
53
  help='Variables to be used in the statements.',
55
54
  ),
@@ -1,3 +1,4 @@
1
+ import functools
1
2
  import pathlib
2
3
  import shutil
3
4
  from typing import Dict, List, Optional, Set
@@ -40,12 +41,44 @@ def _compile_generator(generator: CodeItem) -> str:
40
41
  return compile_item(generator, sanitized=SanitizationLevel.PREFER)
41
42
 
42
43
 
44
+ @functools.cache
45
+ def _warn_once_about_crlf():
46
+ console.console.print(
47
+ '[warning]It seems a few files have CRLF (\\r\\n) line endings.[/warning]'
48
+ )
49
+ console.console.print(
50
+ '[warning]This usually happens when the file is created on Windows. Please convert the file to LF (\\n) line endings.[/warning]'
51
+ )
52
+ console.console.print(
53
+ '[warning]If you are in VSCode, you can make sure LF (\\n) line endings are used by changing the [item]"files.eol"[/item] setting.[/warning]'
54
+ )
55
+
56
+
57
+ @functools.cache
58
+ def _warn_about_crlf(path: pathlib.Path):
59
+ _warn_once_about_crlf()
60
+ console.console.print(
61
+ f'[warning]Testcase file [item]{path}[/item] has CRLF (\\r\\n) line endings, converting to LF (\\n).[/warning]'
62
+ )
63
+
64
+
65
+ def _check_crlf(path: pathlib.Path):
66
+ with open(path, 'rb') as f:
67
+ for line in f:
68
+ if line.endswith(b'\r\n') or line.endswith(b'\n\r'):
69
+ _warn_about_crlf(path)
70
+ break
71
+
72
+ path.write_text(path.read_text().replace('\r', ''))
73
+
74
+
43
75
  def _copy_testcase_over(
44
76
  testcase: Testcase,
45
77
  dest: Testcase,
46
78
  ):
47
79
  testcase = fill_output_for_defined_testcase(testcase)
48
80
  dest.inputPath.parent.mkdir(parents=True, exist_ok=True)
81
+ _check_crlf(testcase.inputPath)
49
82
  shutil.copy(
50
83
  str(testcase.inputPath),
51
84
  str(dest.inputPath),
@@ -55,6 +88,7 @@ def _copy_testcase_over(
55
88
  and testcase.outputPath.is_file()
56
89
  and dest.outputPath is not None
57
90
  ):
91
+ _check_crlf(testcase.outputPath)
58
92
  dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
59
93
  shutil.copy(
60
94
  str(testcase.outputPath),
@@ -71,6 +105,8 @@ def _copy_testcase_output_over(
71
105
  if not src_path.is_file():
72
106
  return False
73
107
 
108
+ _check_crlf(src_path)
109
+
74
110
  shutil.copy(str(src_path), str(dest_output_path.with_suffix(suffix)))
75
111
  return True
76
112
 
@@ -84,6 +120,7 @@ def _copy_testcase_outputs_over(
84
120
  has_copied = False
85
121
 
86
122
  if testcase.outputPath is not None and testcase.outputPath.is_file():
123
+ _check_crlf(testcase.outputPath)
87
124
  shutil.copy(str(testcase.outputPath), str(dest.outputPath))
88
125
  has_copied = True
89
126
 
@@ -120,35 +157,42 @@ def get_call_from_string(call_str: str) -> GeneratorCall:
120
157
  async def _get_necessary_generators_for_groups(
121
158
  groups: Optional[Set[str]] = None,
122
159
  ) -> Set[str]:
123
- pkg = package.find_problem_package_or_die()
124
- existing_generators = set(generator.name for generator in pkg.generators)
125
160
  necessary_generators = set()
126
161
 
127
162
  class NecessaryGeneratorsVisitor(TestcaseGroupVisitor):
128
163
  async def visit(self, entry: GenerationTestcaseEntry):
129
164
  if entry.metadata.generator_call is not None:
165
+ if (
166
+ package.get_generator_or_nil(entry.metadata.generator_call.name)
167
+ is None
168
+ ):
169
+ console.console.print(
170
+ f'[error]Generator [item]{entry.metadata.generator_call.name}[/item] is not present in the package.[/error]'
171
+ )
172
+ if entry.metadata.generator_script is not None:
173
+ console.console.print(
174
+ f'[error]This generator is referenced from [item]{entry.metadata.generator_script}[/item].[/error]'
175
+ )
176
+ raise typer.Exit(1)
130
177
  necessary_generators.add(entry.metadata.generator_call.name)
131
178
 
132
179
  await run_testcase_visitor(NecessaryGeneratorsVisitor(groups))
133
180
 
134
- return existing_generators.intersection(necessary_generators)
181
+ return necessary_generators
135
182
 
136
183
 
137
184
  def compile_generators(
185
+ tracked_generators: Set[str],
138
186
  progress: Optional[StatusProgress] = None,
139
- tracked_generators: Optional[Set[str]] = None,
140
187
  ) -> Dict[str, str]:
141
188
  def update_status(text: str):
142
189
  if progress is not None:
143
190
  progress.update(text)
144
191
 
145
- pkg = package.find_problem_package_or_die()
146
-
147
192
  generator_to_compiled_digest = {}
148
193
 
149
- for generator in pkg.generators:
150
- if tracked_generators is not None and generator.name not in tracked_generators:
151
- continue
194
+ for generator_name in tracked_generators:
195
+ generator = package.get_generator(generator_name)
152
196
  update_status(f'Compiling generator [item]{generator.name}[/item]')
153
197
  try:
154
198
  generator_to_compiled_digest[generator.name] = _compile_generator(generator)
@@ -267,9 +311,7 @@ async def generate_testcases(
267
311
 
268
312
  compiled_generators = compile_generators(
269
313
  progress=progress,
270
- tracked_generators=await _get_necessary_generators_for_groups(groups)
271
- if groups is not None
272
- else None,
314
+ tracked_generators=await _get_necessary_generators_for_groups(groups),
273
315
  )
274
316
 
275
317
  testcase_utils.clear_built_testcases()
@@ -229,13 +229,45 @@ def get_build_testgroup_path(
229
229
 
230
230
 
231
231
  @functools.cache
232
- def get_generator(name: str, root: pathlib.Path = pathlib.Path()) -> Generator:
232
+ def get_generator_or_nil(
233
+ name: str, root: pathlib.Path = pathlib.Path()
234
+ ) -> Optional[Generator]:
233
235
  package = find_problem_package_or_die(root)
234
236
  for generator in package.generators:
235
237
  if generator.name == name:
236
238
  return generator
237
- console.console.print(f'[error]Generator [item]{name}[/item] not found[/error]')
238
- raise typer.Exit(1)
239
+
240
+ path = pathlib.Path(root / name)
241
+ if path.is_file():
242
+ return Generator(name=name, path=path)
243
+
244
+ path_pattern = path.with_suffix('.*')
245
+ matching_files = list(
246
+ file.relative_to(root) for file in root.glob(str(path_pattern))
247
+ )
248
+
249
+ if len(matching_files) > 1:
250
+ console.console.print(
251
+ f'[error]Multiple candidate generators found for [item]{name}[/item]: {matching_files}[/error]'
252
+ )
253
+ console.console.print(
254
+ '[info]Please specify the generator path explicitly, including the extension, or rename the conflicting files.[/info]'
255
+ )
256
+ raise typer.Exit(1)
257
+
258
+ if matching_files:
259
+ return Generator(name=name, path=matching_files[0])
260
+
261
+ return None
262
+
263
+
264
+ @functools.cache
265
+ def get_generator(name: str, root: pathlib.Path = pathlib.Path()) -> Generator:
266
+ generator = get_generator_or_nil(name, root)
267
+ if generator is None:
268
+ console.console.print(f'[error]Generator [item]{name}[/item] not found[/error]')
269
+ raise typer.Exit(1)
270
+ return generator
239
271
 
240
272
 
241
273
  @functools.cache
@@ -292,7 +324,27 @@ def get_interactor(root: pathlib.Path = pathlib.Path()) -> CodeItem:
292
324
  @functools.cache
293
325
  def get_solutions(root: pathlib.Path = pathlib.Path()) -> List[Solution]:
294
326
  package = find_problem_package_or_die(root)
295
- return package.solutions
327
+ seen_paths = set()
328
+ res = []
329
+
330
+ def add_solution(entry: Solution):
331
+ if entry.path in seen_paths:
332
+ return
333
+ seen_paths.add(entry.path)
334
+ res.append(entry)
335
+
336
+ for entry in package.solutions:
337
+ if '*' in str(entry.path):
338
+ for file in root.glob(str(entry.path)):
339
+ relative_file = file.relative_to(root)
340
+ add_solution(
341
+ Solution.model_copy(
342
+ entry, update={'path': relative_file}, deep=True
343
+ )
344
+ )
345
+ continue
346
+ add_solution(entry)
347
+ return res
296
348
 
297
349
 
298
350
  @functools.cache
@@ -214,8 +214,7 @@ def _upload_testcases(problem: api.Problem):
214
214
 
215
215
  def _upload_solutions(problem: api.Problem):
216
216
  console.console.print('Uploading main solution...')
217
- pkg = package.find_problem_package_or_die()
218
- main_solution = pkg.solutions[0]
217
+ main_solution = package.get_main_solution()
219
218
  if main_solution is None or main_solution.outcome != ExpectedOutcome.ACCEPTED:
220
219
  return
221
220
  problem.save_solution(
@@ -225,7 +224,7 @@ def _upload_solutions(problem: api.Problem):
225
224
  tag=api.SolutionTag.MA,
226
225
  )
227
226
 
228
- for i, solution in enumerate(pkg.solutions):
227
+ for i, solution in enumerate(package.get_solutions()):
229
228
  console.console.print(
230
229
  f'Uploading solution [item]{solution.path.name}[/item] (tag: [item]{_get_solution_tag(solution, is_first=i == 0)}[/item])...'
231
230
  )
@@ -9,7 +9,7 @@ from pydantic import AfterValidator, BaseModel, ConfigDict, Field, model_validat
9
9
  from pydantic_core import PydanticCustomError
10
10
 
11
11
  from rbx.autoenum import AutoEnum, alias
12
- from rbx.box.fields import FNameField, NameField
12
+ from rbx.box.fields import NameField
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
@@ -257,7 +257,7 @@ class Testcase(BaseModel):
257
257
  class GeneratorCall(BaseModel):
258
258
  model_config = ConfigDict(extra='forbid')
259
259
 
260
- name: str = FNameField(description='The name of the generator to call.')
260
+ name: str = Field(description='The name of the generator to call.')
261
261
 
262
262
  args: Optional[str] = Field(
263
263
  default=None, description='The arguments to pass to the generator.'
@@ -355,7 +355,7 @@ problems that have points.
355
355
  class Generator(CodeItem):
356
356
  model_config = ConfigDict(extra='forbid')
357
357
 
358
- name: str = NameField(description="""The name of the generator.""")
358
+ name: str = Field(description="""The name of the generator.""")
359
359
 
360
360
 
361
361
  class Solution(CodeItem):
@@ -158,12 +158,10 @@ def compile_solutions(
158
158
  tracked_solutions: Optional[Set[str]] = None,
159
159
  sanitized: bool = False,
160
160
  ) -> Dict[pathlib.Path, str]:
161
- pkg = package.find_problem_package_or_die()
162
-
163
161
  compiled_solutions = {}
164
162
 
165
163
  if tracked_solutions is None:
166
- tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
164
+ tracked_solutions = set(str(sol.path) for sol in package.get_solutions())
167
165
 
168
166
  for solution in expand_solutions(list(tracked_solutions)):
169
167
  if progress:
@@ -232,9 +230,8 @@ async def convert_list_of_solution_evaluations_to_dict(
232
230
  skeleton: SolutionReportSkeleton,
233
231
  items: Iterable[EvaluationItem],
234
232
  ) -> List[Dict[str, List[Evaluation]]]:
235
- pkg = package.find_problem_package_or_die()
236
233
  res: List[Dict[str, List[Evaluation]]] = [
237
- collections.defaultdict(list) for _ in pkg.solutions
234
+ collections.defaultdict(list) for _ in package.get_solutions()
238
235
  ]
239
236
 
240
237
  for item in items:
@@ -250,10 +247,9 @@ def _get_solutions_for_skeleton(
250
247
  tracked_solutions: Optional[Iterable[str]] = None,
251
248
  verification: VerificationLevel = VerificationLevel.NONE,
252
249
  ) -> List[Solution]:
253
- pkg = package.find_problem_package_or_die()
254
250
  solutions = [
255
251
  sol
256
- for sol in pkg.solutions
252
+ for sol in package.get_solutions()
257
253
  if verification.value >= VerificationLevel.ALL_SOLUTIONS.value or is_fast(sol)
258
254
  ]
259
255
  if tracked_solutions is not None:
@@ -739,8 +735,7 @@ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
739
735
 
740
736
 
741
737
  def expand_solutions_with_source(sols: List[str]) -> List[Tuple[Solution, bool]]:
742
- pkg = package.find_problem_package_or_die()
743
- pkg_sols = {str(sol.path): sol for sol in pkg.solutions}
738
+ pkg_sols = {str(sol.path): sol for sol in package.get_solutions()}
744
739
 
745
740
  # Download remote sols.
746
741
  path_sols = remote.expand_files(sols)
@@ -777,20 +772,21 @@ async def pick_solutions(
777
772
  tracked_solutions: Optional[OrderedSet[str]],
778
773
  extra_solutions: Optional[List[str]] = None,
779
774
  ) -> List[str]:
780
- pkg = package.find_problem_package_or_die()
781
775
  # Store in a separate list to maintain order with the package declaration.
782
776
  import questionary
783
777
 
778
+ solutions = package.get_solutions()
779
+
784
780
  choices = [
785
781
  questionary.Choice(
786
782
  title=_get_solution_repr(sol),
787
783
  value=str(sol.path),
788
784
  checked=tracked_solutions is None or str(sol.path) in tracked_solutions,
789
785
  )
790
- for sol in pkg.solutions
786
+ for sol in solutions
791
787
  ]
792
788
 
793
- seen_sols = set(str(sol.path) for sol in pkg.solutions)
789
+ seen_sols = set(str(sol.path) for sol in solutions)
794
790
 
795
791
  if extra_solutions is not None:
796
792
  # Add only new solutions.
@@ -339,7 +339,6 @@ async def build(
339
339
  vars: Annotated[
340
340
  Optional[List[str]],
341
341
  typer.Option(
342
- '-v',
343
342
  '--vars',
344
343
  help='Variables to be used in the statements.',
345
344
  ),
@@ -5,6 +5,8 @@ from typing import Optional
5
5
 
6
6
  import chardet
7
7
 
8
+ from rbx.utils import command_exists
9
+
8
10
  MAX_PDFLATEX_RUNS = 3
9
11
 
10
12
 
@@ -45,3 +47,12 @@ class Latex:
45
47
  return LatexResult(result=completed, pdf=None)
46
48
 
47
49
  return LatexResult(result=completed, pdf=output_path.read_bytes())
50
+
51
+
52
+ def install_tex_packages(path: pathlib.Path, cwd: pathlib.Path):
53
+ if not command_exists('texliveonfly'):
54
+ return
55
+ subprocess.run(
56
+ ['texliveonfly', path],
57
+ cwd=cwd,
58
+ )
@@ -241,6 +241,7 @@ def _copy_hashed_files(artifact_list: List[GradingArtifacts], cacher: FileCacher
241
241
  ) is not None:
242
242
  # Use a symlink to the file in the persistent cache, if available.
243
243
  output.dest.unlink(missing_ok=True)
244
+ output.dest.parent.mkdir(parents=True, exist_ok=True)
244
245
  output.dest.symlink_to(path_to_symlink)
245
246
  else:
246
247
  # Otherwise, copy it.
@@ -469,6 +469,7 @@ class SandboxBase(abc.ABC):
469
469
  if override:
470
470
  real_path.unlink(missing_ok=True)
471
471
  try:
472
+ real_path.parent.mkdir(parents=True, exist_ok=True)
472
473
  real_path.symlink_to(utils.abspath(from_path))
473
474
  except NotImplementedError:
474
475
  return None
@@ -355,6 +355,7 @@ def _process_output_artifacts(
355
355
  ):
356
356
  # File is in the persistent cache, store a symlink to it.
357
357
  dst.unlink(missing_ok=True)
358
+ dst.parent.mkdir(parents=True, exist_ok=True)
358
359
  dst.symlink_to(path_to_symlink)
359
360
  else:
360
361
  # File is not in the persistent cache, copy it.
@@ -0,0 +1,21 @@
1
+ .box/
2
+ build/
3
+ __pycache__/
4
+
5
+ .DS_Store
6
+ .vscode/
7
+
8
+ a.out
9
+ *.exe
10
+ *.pyc
11
+ *.o
12
+ *~
13
+
14
+ *.fdb_latexmk
15
+ *.fls
16
+ *.log
17
+ *.synctex.gz
18
+ *.aux
19
+ *.log
20
+
21
+ *.un~
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  # yaml-language-server: $schema=https://rsalesc.github.io/rbx/schemas/Contest.json
3
- # Add problems by running `rbx contest add <problem-name> <short-name>`
3
+ # Add problems by running `rbx contest add`
4
4
  name: "new-contest"
5
5
  statements:
6
6
  - name: "statement-en"
7
7
  title: "New contest"
8
8
  language: "en"
9
9
  path: "statement/contest.rbx.tex"
10
- type: "jinja-tex"
10
+ type: JinjaTeX
11
11
  assets:
12
12
  - "statement/icpc.sty"
13
13
  - "statement/*.png"
@@ -0,0 +1,21 @@
1
+ .box/
2
+ build/
3
+ __pycache__/
4
+
5
+ .DS_Store
6
+ .vscode/
7
+
8
+ a.out
9
+ *.exe
10
+ *.pyc
11
+ *.o
12
+ *~
13
+
14
+ *.fdb_latexmk
15
+ *.fls
16
+ *.log
17
+ *.synctex.gz
18
+ *.aux
19
+ *.log
20
+
21
+ *.un~
@@ -1,3 +1,4 @@
1
+ #include "rbx.h"
1
2
  #include "testlib.h"
2
3
 
3
4
  using namespace std;
@@ -6,7 +7,7 @@ int main(int argc, char *argv[]) {
6
7
  registerValidation(argc, argv);
7
8
  prepareOpts(argc, argv);
8
9
 
9
- int MAX_N = opt<int>("MAX_N"); // Read from package vars.
10
+ int MAX_N = getVar<int>("MAX_N"); // Read from package vars.
10
11
 
11
12
  inf.readInt(1, MAX_N, "A");
12
13
  inf.readSpace();
@@ -220,7 +220,7 @@
220
220
  \end{minipage}
221
221
  \end{tabular}
222
222
  \end{center}
223
- \vspace{-0.5cm}
223
+ \vspace{-0.1cm}
224
224
  }
225
225
  } % exampleInteractive
226
226
 
@@ -6,6 +6,7 @@ import os
6
6
  import os.path
7
7
  import pathlib
8
8
  import resource
9
+ import subprocess
9
10
  from typing import Any, Optional, Type, TypeVar
10
11
 
11
12
  import rich
@@ -145,6 +146,18 @@ def get_open_fds():
145
146
  return fds
146
147
 
147
148
 
149
+ def command_exists(command):
150
+ try:
151
+ subprocess.run(
152
+ [command], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
153
+ )
154
+ return True
155
+ except FileNotFoundError:
156
+ return False
157
+ except subprocess.CalledProcessError:
158
+ return True
159
+
160
+
148
161
  @contextlib.contextmanager
149
162
  def new_cd(x: pathlib.Path):
150
163
  d = os.getcwd()
@@ -1,6 +0,0 @@
1
- .box/
2
- build/
3
-
4
- *.o
5
- *.aux
6
- *.log
@@ -1,6 +0,0 @@
1
- .box/
2
- build/
3
-
4
- *.o
5
- *.aux
6
- *.log
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes