rbx.cp 0.11.2__tar.gz → 0.13.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/PKG-INFO +2 -2
  2. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/pyproject.toml +2 -2
  3. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/builder.py +3 -3
  4. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/cli.py +9 -0
  5. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/build_contest_statements.py +5 -6
  6. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/statements.py +0 -1
  7. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/generators.py +93 -23
  8. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/header.py +5 -0
  9. rbx_cp-0.13.2/rbx/box/lang.py +40 -0
  10. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/package.py +56 -4
  11. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/contest_main.py +40 -7
  12. rbx_cp-0.13.2/rbx/box/packaging/importer.py +37 -0
  13. rbx_cp-0.13.2/rbx/box/packaging/main.py +98 -0
  14. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/packager.py +95 -2
  15. rbx_cp-0.13.2/rbx/box/packaging/polygon/importer.py +232 -0
  16. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/polygon/packager.py +36 -5
  17. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/polygon/upload.py +34 -14
  18. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/polygon/xml_schema.py +15 -6
  19. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/schema.py +3 -3
  20. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/solutions.py +8 -12
  21. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/build_statements.py +0 -1
  22. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/latex.py +11 -0
  23. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/stresses.py +1 -1
  24. rbx_cp-0.13.2/rbx/box/tooling/converter.py +76 -0
  25. rbx_cp-0.13.2/rbx/box/tooling/main.py +61 -0
  26. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/caching.py +1 -0
  27. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/sandbox.py +1 -0
  28. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/steps.py +1 -0
  29. rbx_cp-0.13.2/rbx/resources/presets/default/contest/.gitignore +21 -0
  30. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/contest/contest.rbx.yml +2 -2
  31. rbx_cp-0.13.2/rbx/resources/presets/default/problem/.gitignore +21 -0
  32. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/problem.rbx.yml +1 -4
  33. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/testplan/random.py +1 -1
  34. rbx_cp-0.13.2/rbx/resources/presets/default/problem/testplan/random.txt +2 -0
  35. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/validator.cpp +2 -1
  36. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/shared/icpc.sty +1 -1
  37. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/utils.py +13 -0
  38. rbx_cp-0.11.2/rbx/box/lang.py +0 -27
  39. rbx_cp-0.11.2/rbx/box/packaging/main.py +0 -145
  40. rbx_cp-0.11.2/rbx/box/tooling/main.py +0 -8
  41. rbx_cp-0.11.2/rbx/resources/presets/default/contest/.gitignore +0 -6
  42. rbx_cp-0.11.2/rbx/resources/presets/default/problem/.gitignore +0 -6
  43. rbx_cp-0.11.2/rbx/resources/presets/default/problem/testplan/random.txt +0 -4
  44. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/LICENSE +0 -0
  45. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/README.md +0 -0
  46. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/__init__.py +0 -0
  47. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/annotations.py +0 -0
  48. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/autoenum.py +0 -0
  49. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/__init__.py +0 -0
  50. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/cd.py +0 -0
  51. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/checkers.py +0 -0
  52. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/code.py +0 -0
  53. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/compile.py +0 -0
  54. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/conftest.py +0 -0
  55. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/__init__.py +0 -0
  56. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/contest_package.py +0 -0
  57. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/contest_utils.py +0 -0
  58. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/main.py +0 -0
  59. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/contest/schema.py +0 -0
  60. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/creation.py +0 -0
  61. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/deferred.py +0 -0
  62. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/download.py +0 -0
  63. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/dump_schemas.py +0 -0
  64. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/environment.py +0 -0
  65. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/extensions.py +0 -0
  66. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/fields.py +0 -0
  67. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/formatting.py +0 -0
  68. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/generators_test.py +0 -0
  69. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/git_utils.py +0 -0
  70. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/global_package.py +0 -0
  71. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/lazy_importing_main.py +0 -0
  72. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/lazy_importing_test.py +0 -0
  73. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/linting.py +0 -0
  74. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/main.py +0 -0
  75. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/naming.py +0 -0
  76. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/__init__.py +0 -0
  77. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/boca/__init__.py +0 -0
  78. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/boca/extension.py +0 -0
  79. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/boca/packager.py +0 -0
  80. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/moj/packager.py +0 -0
  81. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/pkg/packager.py +0 -0
  82. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  83. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/packaging/polygon/test.py +0 -0
  84. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/presets/__init__.py +0 -0
  85. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/presets/fetch.py +0 -0
  86. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/presets/lock_schema.py +0 -0
  87. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/presets/schema.py +0 -0
  88. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/remote.py +0 -0
  89. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/retries.py +0 -0
  90. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/sanitizers/warning_stack.py +0 -0
  91. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/setter_config.py +0 -0
  92. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/solutions_test.py +0 -0
  93. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/state.py +0 -0
  94. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/__init__.py +0 -0
  95. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/builders.py +0 -0
  96. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/expander.py +0 -0
  97. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/joiners.py +0 -0
  98. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/latex_jinja.py +0 -0
  99. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/statements/schema.py +0 -0
  100. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/stats.py +0 -0
  101. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/stressing/__init__.py +0 -0
  102. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/stressing/finder_parser.py +0 -0
  103. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/stressing/generator_parser.py +0 -0
  104. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tasks.py +0 -0
  105. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/testcase_extractors.py +0 -0
  106. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/testcase_utils.py +0 -0
  107. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/testcases/__init__.py +0 -0
  108. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/testcases/main.py +0 -0
  109. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tooling/__init__.py +0 -0
  110. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tooling/boca/__init__.py +0 -0
  111. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tooling/boca/main.py +0 -0
  112. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tooling/boca/scrape.py +0 -0
  113. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/tooling/boca/scraper.py +0 -0
  114. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/__init__.py +0 -0
  115. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/captured_log.py +0 -0
  116. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/css/app.tcss +0 -0
  117. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/main.py +0 -0
  118. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/__init__.py +0 -0
  119. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/build.py +0 -0
  120. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/command.py +0 -0
  121. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/differ.py +0 -0
  122. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/error.py +0 -0
  123. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/rich_log_modal.py +0 -0
  124. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/run.py +0 -0
  125. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/run_explorer.py +0 -0
  126. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  127. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/selector.py +0 -0
  128. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/screens/test_explorer.py +0 -0
  129. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/utils/__init__.py +0 -0
  130. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/utils/run_ui.py +0 -0
  131. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/__init__.py +0 -0
  132. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/diff_box.py +0 -0
  133. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/file_log.py +0 -0
  134. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/interaction_box.py +0 -0
  135. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  136. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/test_output_box.py +0 -0
  137. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  138. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/unit.py +0 -0
  139. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/validators.py +0 -0
  140. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/box/validators_test.py +0 -0
  141. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/checker.py +0 -0
  142. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/clone.py +0 -0
  143. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/config.py +0 -0
  144. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/conftest.py +0 -0
  145. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/console.py +0 -0
  146. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/create.py +0 -0
  147. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/edit.py +0 -0
  148. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/__init__.py +0 -0
  149. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/conftest.py +0 -0
  150. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/debug_context.py +0 -0
  151. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/grading_context.py +0 -0
  152. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/__init__.py +0 -0
  153. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/cacher.py +0 -0
  154. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/digester.py +0 -0
  155. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  156. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  157. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  158. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  159. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/storage.py +0 -0
  160. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/test.py +0 -0
  161. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/judge/testiso.py +0 -0
  162. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/limits.py +0 -0
  163. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/processing_context.py +0 -0
  164. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/profiling.py +0 -0
  165. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/steps_with_caching.py +0 -0
  166. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading/steps_with_caching_run_test.py +0 -0
  167. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/grading_utils.py +0 -0
  168. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/hydration.py +0 -0
  169. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/main.py +0 -0
  170. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/metadata.py +0 -0
  171. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/providers/__init__.py +0 -0
  172. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/providers/codeforces.py +0 -0
  173. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/providers/provider.py +0 -0
  174. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/checkers/boilerplate.cpp +0 -0
  175. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/checkers/noop.cpp +0 -0
  176. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/default_config.json +0 -0
  177. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/default_setter_config.mac.yml +0 -0
  178. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/default_setter_config.yml +0 -0
  179. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/envs/default.rbx.yml +0 -0
  180. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/envs/isolate.rbx.yml +0 -0
  181. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/checker.sh +0 -0
  182. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compare.sh +0 -0
  183. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/c +0 -0
  184. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/cc +0 -0
  185. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/cpp +0 -0
  186. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/java +0 -0
  187. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/kt +0 -0
  188. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/py2 +0 -0
  189. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/compile/py3 +0 -0
  190. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/c +0 -0
  191. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/cc +0 -0
  192. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  193. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/java +0 -0
  194. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/kt +0 -0
  195. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  196. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  197. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  198. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/interactor_run.sh +0 -0
  199. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/bkp +0 -0
  200. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/c +0 -0
  201. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/cc +0 -0
  202. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/cpp +0 -0
  203. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/java +0 -0
  204. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/kt +0 -0
  205. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/py2 +0 -0
  206. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/run/py3 +0 -0
  207. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/safeexec.c +0 -0
  208. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/boca/safeexec_compile.sh +0 -0
  209. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  210. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  211. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  212. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  213. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  214. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  215. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  216. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  217. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  218. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  219. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  220. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  221. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  222. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  223. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  224. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  225. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  226. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  227. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  228. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/contest/statement/instructions.tex +0 -0
  229. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/contest/statement/logo.png +0 -0
  230. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/env.rbx.yml +0 -0
  231. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  232. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/gens/gen.cpp +0 -0
  233. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/manual_tests/samples/000.in +0 -0
  234. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/manual_tests/samples/001.in +0 -0
  235. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/rbx.h +0 -0
  236. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  237. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  238. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  239. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  240. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  241. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/shared/contest_template.rbx.tex +0 -0
  242. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/presets/default/shared/problem_template.rbx.tex +0 -0
  243. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/templates/rbx.h +0 -0
  244. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/resources/templates/template.cpp +0 -0
  245. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/run.py +0 -0
  246. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/schema.py +0 -0
  247. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/submit.py +0 -0
  248. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/submitors/__init__.py +0 -0
  249. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/submitors/codeforces.py +0 -0
  250. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/submitors/submitor.py +0 -0
  251. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/test.py +0 -0
  252. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/testcase.py +0 -0
  253. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/rbx/testcase_rendering.py +0 -0
  254. {rbx_cp-0.11.2 → rbx_cp-0.13.2}/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.13.2
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9.1,<4.0.0
@@ -20,6 +20,7 @@ Requires-Dist: fastapi (>=0.115.8,<0.116.0)
20
20
  Requires-Dist: filelock (>=3.14.0,<4.0.0)
21
21
  Requires-Dist: gitignore-parser (>=0.1.12,<0.2.0)
22
22
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
23
+ Requires-Dist: iso639-lang (>=2.6.1,<3.0.0)
23
24
  Requires-Dist: jinja2 (>=3.1.4,<4.0.0)
24
25
  Requires-Dist: lark (>=1.2.2,<2.0.0)
25
26
  Requires-Dist: latexbuild (>=0.2.2,<0.3.0)
@@ -33,7 +34,6 @@ Requires-Dist: pydantic (==2.8.2)
33
34
  Requires-Dist: pydantic-xml[lxml] (>=2.11.0,<3.0.0)
34
35
  Requires-Dist: pypandoc (>=1.15,<2.0)
35
36
  Requires-Dist: pyte (>=0.8.2,<0.9.0)
36
- Requires-Dist: python-iso639 (>=2024.4.27,<2025.0.0)
37
37
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
38
38
  Requires-Dist: questionary (>=2.1.0,<3.0.0)
39
39
  Requires-Dist: requests (>=2.32.3,<3.0.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.11.2"
3
+ version = "0.13.2"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -24,7 +24,6 @@ pyyaml = "^6.0.1"
24
24
  requests = "^2.32.3"
25
25
  latexbuild = "^0.2.2"
26
26
  pydantic-xml = {extras = ["lxml"], version = "^2.11.0"}
27
- python-iso639 = "^2024.4.27"
28
27
  more-itertools = "^10.5.0"
29
28
  gitpython = "^3.1.43"
30
29
  textual = "^3.1.1"
@@ -49,6 +48,7 @@ lz4 = "^4.4.4"
49
48
  ordered-set = "^4.1.0"
50
49
  gitignore-parser = "^0.1.12"
51
50
  pypandoc = "^1.15"
51
+ iso639-lang = "^2.6.1"
52
52
 
53
53
  [tool.poetry.scripts]
54
54
  rbc = "rbx.main:app"
@@ -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:
@@ -186,6 +186,15 @@ def on(ctx: typer.Context, problems: str) -> None:
186
186
  contest.on(ctx, problems)
187
187
 
188
188
 
189
+ @app.command(
190
+ 'each',
191
+ help='Run a command for each problem in the contest.',
192
+ context_settings={'allow_extra_args': True, 'ignore_unknown_options': True},
193
+ )
194
+ def each(ctx: typer.Context) -> None:
195
+ contest.each(ctx)
196
+
197
+
189
198
  @app.command('diff', hidden=True)
190
199
  def diff(path1: pathlib.Path, path2: pathlib.Path):
191
200
  from rbx.box.ui import main as ui_pkg
@@ -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),
@@ -63,7 +97,10 @@ def _copy_testcase_over(
63
97
 
64
98
 
65
99
  def _copy_testcase_output_over(
66
- src_output_path: pathlib.Path, dest_output_path: pathlib.Path, suffix: str
100
+ src_output_path: pathlib.Path,
101
+ dest_output_path: pathlib.Path,
102
+ suffix: str,
103
+ dry_run: bool = False,
67
104
  ) -> bool:
68
105
  dest_output_path.parent.mkdir(parents=True, exist_ok=True)
69
106
 
@@ -71,38 +108,64 @@ def _copy_testcase_output_over(
71
108
  if not src_path.is_file():
72
109
  return False
73
110
 
111
+ if dry_run:
112
+ return True
113
+
114
+ _check_crlf(src_path)
74
115
  shutil.copy(str(src_path), str(dest_output_path.with_suffix(suffix)))
75
116
  return True
76
117
 
77
118
 
78
119
  def _copy_testcase_outputs_over(
79
- testcase: Testcase, dest: Testcase, pipes: bool = False
120
+ testcase: Testcase, dest: Testcase, pipes: bool = False, dry_run: bool = False
80
121
  ):
81
122
  assert dest.outputPath is not None
82
- dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
123
+ if not dry_run:
124
+ dest.outputPath.parent.mkdir(parents=True, exist_ok=True)
83
125
 
84
126
  has_copied = False
85
127
 
86
128
  if testcase.outputPath is not None and testcase.outputPath.is_file():
87
- shutil.copy(str(testcase.outputPath), str(dest.outputPath))
129
+ if not dry_run:
130
+ _check_crlf(testcase.outputPath)
131
+ shutil.copy(str(testcase.outputPath), str(dest.outputPath))
88
132
  has_copied = True
89
133
 
90
134
  if not pipes:
91
135
  return has_copied
92
136
 
93
137
  reference_path = testcase.outputPath or testcase.inputPath
94
- if _copy_testcase_output_over(reference_path, dest.outputPath, '.pin'):
138
+ if _copy_testcase_output_over(
139
+ reference_path, dest.outputPath, '.pin', dry_run=dry_run
140
+ ):
95
141
  has_copied = True
96
142
 
97
- if _copy_testcase_output_over(reference_path, dest.outputPath, '.pout'):
143
+ if _copy_testcase_output_over(
144
+ reference_path, dest.outputPath, '.pout', dry_run=dry_run
145
+ ):
98
146
  has_copied = True
99
147
 
100
- if _copy_testcase_output_over(reference_path, dest.outputPath, '.pio'):
148
+ if _copy_testcase_output_over(
149
+ reference_path, dest.outputPath, '.pio', dry_run=dry_run
150
+ ):
101
151
  has_copied = True
102
152
 
103
153
  return has_copied
104
154
 
105
155
 
156
+ def _needs_output(generation_entries: List[GenerationTestcaseEntry]) -> bool:
157
+ for entry in generation_entries:
158
+ tc = entry.metadata.copied_to
159
+ if not tc.inputPath.is_file():
160
+ continue
161
+ if entry.metadata.copied_from is not None and _copy_testcase_outputs_over(
162
+ entry.metadata.copied_from, tc, dry_run=True
163
+ ):
164
+ continue
165
+ return True
166
+ return False
167
+
168
+
106
169
  def get_all_built_testcases() -> Dict[str, List[Testcase]]:
107
170
  pkg = package.find_problem_package_or_die()
108
171
  res = {group.name: find_built_testcases(group) for group in pkg.testcases}
@@ -120,35 +183,42 @@ def get_call_from_string(call_str: str) -> GeneratorCall:
120
183
  async def _get_necessary_generators_for_groups(
121
184
  groups: Optional[Set[str]] = None,
122
185
  ) -> Set[str]:
123
- pkg = package.find_problem_package_or_die()
124
- existing_generators = set(generator.name for generator in pkg.generators)
125
186
  necessary_generators = set()
126
187
 
127
188
  class NecessaryGeneratorsVisitor(TestcaseGroupVisitor):
128
189
  async def visit(self, entry: GenerationTestcaseEntry):
129
190
  if entry.metadata.generator_call is not None:
191
+ if (
192
+ package.get_generator_or_nil(entry.metadata.generator_call.name)
193
+ is None
194
+ ):
195
+ console.console.print(
196
+ f'[error]Generator [item]{entry.metadata.generator_call.name}[/item] is not present in the package.[/error]'
197
+ )
198
+ if entry.metadata.generator_script is not None:
199
+ console.console.print(
200
+ f'[error]This generator is referenced from [item]{entry.metadata.generator_script}[/item].[/error]'
201
+ )
202
+ raise typer.Exit(1)
130
203
  necessary_generators.add(entry.metadata.generator_call.name)
131
204
 
132
205
  await run_testcase_visitor(NecessaryGeneratorsVisitor(groups))
133
206
 
134
- return existing_generators.intersection(necessary_generators)
207
+ return necessary_generators
135
208
 
136
209
 
137
210
  def compile_generators(
211
+ tracked_generators: Set[str],
138
212
  progress: Optional[StatusProgress] = None,
139
- tracked_generators: Optional[Set[str]] = None,
140
213
  ) -> Dict[str, str]:
141
214
  def update_status(text: str):
142
215
  if progress is not None:
143
216
  progress.update(text)
144
217
 
145
- pkg = package.find_problem_package_or_die()
146
-
147
218
  generator_to_compiled_digest = {}
148
219
 
149
- for generator in pkg.generators:
150
- if tracked_generators is not None and generator.name not in tracked_generators:
151
- continue
220
+ for generator_name in tracked_generators:
221
+ generator = package.get_generator(generator_name)
152
222
  update_status(f'Compiling generator [item]{generator.name}[/item]')
153
223
  try:
154
224
  generator_to_compiled_digest[generator.name] = _compile_generator(generator)
@@ -267,9 +337,7 @@ async def generate_testcases(
267
337
 
268
338
  compiled_generators = compile_generators(
269
339
  progress=progress,
270
- tracked_generators=await _get_necessary_generators_for_groups(groups)
271
- if groups is not None
272
- else None,
340
+ tracked_generators=await _get_necessary_generators_for_groups(groups),
273
341
  )
274
342
 
275
343
  testcase_utils.clear_built_testcases()
@@ -353,17 +421,20 @@ async def generate_outputs_for_testcases(
353
421
  if progress is not None:
354
422
  progress.step()
355
423
 
424
+ generation_entries = await extract_generation_testcases(entries)
425
+ needs_output = _needs_output(generation_entries)
426
+
356
427
  main_solution = package.get_main_solution()
357
428
  solution_digest: Optional[str] = None
358
429
 
359
430
  pkg = package.find_problem_package_or_die()
360
431
 
361
- if pkg.type == TaskType.COMMUNICATION:
432
+ if pkg.type == TaskType.COMMUNICATION and needs_output:
362
433
  interactor_digest = checkers.compile_interactor(progress)
363
434
  else:
364
435
  interactor_digest = None
365
436
 
366
- if main_solution is not None:
437
+ if main_solution is not None and needs_output:
367
438
  if progress:
368
439
  progress.update('Compiling main solution...')
369
440
  try:
@@ -376,8 +447,6 @@ async def generate_outputs_for_testcases(
376
447
  shutil.rmtree(str(gen_runs_dir), ignore_errors=True)
377
448
  gen_runs_dir.mkdir(parents=True, exist_ok=True)
378
449
 
379
- generation_entries = await extract_generation_testcases(entries)
380
-
381
450
  for entry in generation_entries:
382
451
  tc = entry.metadata.copied_to
383
452
  if not tc.inputPath.is_file():
@@ -392,6 +461,7 @@ async def generate_outputs_for_testcases(
392
461
  step()
393
462
  continue
394
463
 
464
+ assert needs_output
395
465
  if (
396
466
  main_solution is None or solution_digest is None
397
467
  ) and not tc.outputPath.is_file():
@@ -16,6 +16,11 @@ def get_header() -> pathlib.Path:
16
16
 
17
17
 
18
18
  def generate_header():
19
+ override_header = pathlib.Path('rbx.override.h')
20
+ if override_header.is_file():
21
+ pathlib.Path('rbx.h').write_bytes(override_header.read_bytes())
22
+ return
23
+
19
24
  with importlib.resources.as_file(
20
25
  importlib.resources.files('rbx') / 'resources' / 'templates' / 'rbx.h'
21
26
  ) as file:
@@ -0,0 +1,40 @@
1
+ import functools
2
+ from typing import Dict, List
3
+
4
+ import iso639
5
+
6
+
7
+ @functools.cache
8
+ def _get_lowercase_name_mapping() -> Dict[str, iso639.Lang]:
9
+ res = {}
10
+ for lang in iso639.iter_langs():
11
+ res[lang.name.lower()] = lang
12
+ return res
13
+
14
+
15
+ def _get_lang_name(lang: str) -> str:
16
+ mapping = _get_lowercase_name_mapping()
17
+ if lang.lower() in mapping:
18
+ return mapping[lang.lower()].name
19
+ return lang
20
+
21
+
22
+ def code_to_lang(lang: str) -> str:
23
+ return iso639.Lang(lang).name.lower()
24
+
25
+
26
+ def code_to_langs(langs: List[str]) -> List[str]:
27
+ return [code_to_lang(lang) for lang in langs]
28
+
29
+
30
+ @functools.cache
31
+ def is_valid_lang_code(lang: str) -> bool:
32
+ return iso639.is_language(lang)
33
+
34
+
35
+ def lang_to_code(lang: str) -> str:
36
+ return iso639.Lang(_get_lang_name(lang)).pt1
37
+
38
+
39
+ def langs_to_code(langs: List[str]) -> List[str]:
40
+ return [lang_to_code(lang) for lang in langs]
@@ -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
@@ -1,6 +1,6 @@
1
1
  import pathlib
2
2
  import tempfile
3
- from typing import Type
3
+ from typing import Optional, Type
4
4
 
5
5
  import syncer
6
6
  import typer
@@ -8,12 +8,13 @@ import typer
8
8
  from rbx import annotations, console
9
9
  from rbx.box import cd, environment, package
10
10
  from rbx.box.contest import build_contest_statements, contest_package
11
- from rbx.box.packaging.main import run_packager
12
11
  from rbx.box.packaging.packager import (
13
12
  BaseContestPackager,
14
13
  BasePackager,
15
14
  BuiltContestStatement,
16
15
  BuiltProblemPackage,
16
+ ContestZipper,
17
+ run_packager,
17
18
  )
18
19
 
19
20
  app = typer.Typer(no_args_is_help=True, cls=annotations.AliasGroup)
@@ -23,6 +24,7 @@ async def run_contest_packager(
23
24
  contest_packager_cls: Type[BaseContestPackager],
24
25
  packager_cls: Type[BasePackager],
25
26
  verification: environment.VerificationParam,
27
+ **kwargs,
26
28
  ):
27
29
  contest = contest_package.find_contest_package_or_die()
28
30
 
@@ -34,7 +36,9 @@ async def run_contest_packager(
34
36
  )
35
37
  with cd.new_package_cd(problem.get_path()):
36
38
  package.clear_package_cache()
37
- package_path = await run_packager(packager_cls, verification=verification)
39
+ package_path = await run_packager(
40
+ packager_cls, verification=verification, **kwargs
41
+ )
38
42
  built_packages.append(
39
43
  BuiltProblemPackage(
40
44
  path=problem.get_path() / package_path,
@@ -44,7 +48,7 @@ async def run_contest_packager(
44
48
  )
45
49
 
46
50
  # Build statements.
47
- packager = contest_packager_cls()
51
+ packager = contest_packager_cls(**kwargs)
48
52
  statement_types = packager.statement_types()
49
53
  built_statements = []
50
54
 
@@ -63,12 +67,12 @@ async def run_contest_packager(
63
67
 
64
68
  # Build contest-level package.
65
69
  with tempfile.TemporaryDirectory() as td:
66
- packager.package(
70
+ result_path = packager.package(
67
71
  built_packages, pathlib.Path('build'), pathlib.Path(td), built_statements
68
72
  )
69
73
 
70
74
  console.console.print(
71
- f'[success]Contest packaged for [item]{packager.name()}[/item]![/success]'
75
+ f'[success]Created contest package for [item]{packager.name()}[/item] at [item]{result_path}[/item]![/success]'
72
76
  )
73
77
 
74
78
 
@@ -77,6 +81,12 @@ async def run_contest_packager(
77
81
  @syncer.sync
78
82
  async def polygon(
79
83
  verification: environment.VerificationParam,
84
+ language: Optional[str] = typer.Option(
85
+ None,
86
+ '--language',
87
+ '-l',
88
+ help='If set, will use the given language as the main language.',
89
+ ),
80
90
  ):
81
91
  from rbx.box.packaging.polygon.packager import (
82
92
  PolygonContestPackager,
@@ -84,7 +94,30 @@ async def polygon(
84
94
  )
85
95
 
86
96
  await run_contest_packager(
87
- PolygonContestPackager, PolygonPackager, verification=verification
97
+ PolygonContestPackager,
98
+ PolygonPackager,
99
+ verification=verification,
100
+ main_language=language,
101
+ )
102
+
103
+
104
+ @app.command('boca', help='Build a contest package for BOCA.')
105
+ @contest_package.within_contest
106
+ @syncer.sync
107
+ async def boca(
108
+ verification: environment.VerificationParam,
109
+ ):
110
+ from rbx.box.packaging.boca.packager import BocaPackager
111
+
112
+ class BocaContestPackager(ContestZipper):
113
+ def __init__(self, **kwargs):
114
+ super().__init__('boca-contest', zip_inner=True, **kwargs)
115
+
116
+ def name(self) -> str:
117
+ return 'boca'
118
+
119
+ await run_contest_packager(
120
+ BocaContestPackager, BocaPackager, verification=verification
88
121
  )
89
122
 
90
123
 
@@ -0,0 +1,37 @@
1
+ import pathlib
2
+ from abc import ABC, abstractmethod
3
+ from typing import Type
4
+
5
+ from rbx import console
6
+
7
+
8
+ class BaseImporter(ABC):
9
+ @classmethod
10
+ @abstractmethod
11
+ def name(cls) -> str:
12
+ pass
13
+
14
+ @abstractmethod
15
+ async def import_package(self, pkg_path: pathlib.Path, into_path: pathlib.Path):
16
+ pass
17
+
18
+
19
+ class BaseContestImporter(ABC):
20
+ @classmethod
21
+ @abstractmethod
22
+ def name(cls) -> str:
23
+ pass
24
+
25
+ @abstractmethod
26
+ async def import_package(self, pkg_path: pathlib.Path, into_path: pathlib.Path):
27
+ pass
28
+
29
+
30
+ async def run_importer(
31
+ importer_cls: Type[BaseImporter], pkg_path: pathlib.Path, into_path: pathlib.Path
32
+ ):
33
+ importer = importer_cls()
34
+ console.console.print(
35
+ f'Importing package from [item]{pkg_path}[/item] to [item]{into_path}[/item] with [item]{importer.name()}[/item]...'
36
+ )
37
+ await importer.import_package(pkg_path, into_path)