rbx.cp 0.5.64__tar.gz → 0.5.66__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 (220) hide show
  1. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/PKG-INFO +1 -1
  2. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/pyproject.toml +1 -1
  3. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/cli.py +30 -13
  4. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/formatting.py +5 -1
  5. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/package.py +13 -0
  6. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/boca/upload.py +39 -1
  7. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/solutions.py +95 -16
  8. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/testcase_utils.py +6 -1
  9. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/css/app.tcss +14 -0
  10. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/main.py +21 -0
  11. rbx_cp-0.5.66/rbx/box/ui/screens/differ.py +29 -0
  12. rbx_cp-0.5.66/rbx/box/ui/widgets/diff_box.py +38 -0
  13. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/unit.py +0 -2
  14. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/LICENSE +0 -0
  15. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/README.md +0 -0
  16. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/__init__.py +0 -0
  17. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/annotations.py +0 -0
  18. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/autoenum.py +0 -0
  19. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/__init__.py +0 -0
  20. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/builder.py +0 -0
  21. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/cd.py +0 -0
  22. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/checkers.py +0 -0
  23. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/code.py +0 -0
  24. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/compile.py +0 -0
  25. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/conftest.py +0 -0
  26. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/__init__.py +0 -0
  27. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/build_contest_statements.py +0 -0
  28. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/contest_package.py +0 -0
  29. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/contest_utils.py +0 -0
  30. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/main.py +0 -0
  31. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/schema.py +0 -0
  32. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/contest/statements.py +0 -0
  33. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/creation.py +0 -0
  34. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/deferred.py +0 -0
  35. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/download.py +0 -0
  36. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/dump_schemas.py +0 -0
  37. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/environment.py +0 -0
  38. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/extensions.py +0 -0
  39. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/generators.py +0 -0
  40. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/generators_test.py +0 -0
  41. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/git_utils.py +0 -0
  42. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/header.py +0 -0
  43. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/lazy_importing_main.py +0 -0
  44. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/lazy_importing_test.py +0 -0
  45. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/main.py +0 -0
  46. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/naming.py +0 -0
  47. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/boca/extension.py +0 -0
  48. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/boca/packager.py +0 -0
  49. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/contest_main.py +0 -0
  50. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/main.py +0 -0
  51. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/moj/packager.py +0 -0
  52. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/packager.py +0 -0
  53. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/polygon/packager.py +0 -0
  54. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/polygon/polygon_api.py +0 -0
  55. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/polygon/test.py +0 -0
  56. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/polygon/upload.py +0 -0
  57. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/packaging/polygon/xml_schema.py +0 -0
  58. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/presets/__init__.py +0 -0
  59. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/presets/fetch.py +0 -0
  60. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/presets/lock_schema.py +0 -0
  61. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/presets/schema.py +0 -0
  62. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/retries.py +0 -0
  63. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/sanitizers/warning_stack.py +0 -0
  64. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/schema.py +0 -0
  65. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/setter_config.py +0 -0
  66. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/solutions_test.py +0 -0
  67. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/state.py +0 -0
  68. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/__init__.py +0 -0
  69. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/build_statements.py +0 -0
  70. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/builders.py +0 -0
  71. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/joiners.py +0 -0
  72. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/latex.py +0 -0
  73. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/latex_jinja.py +0 -0
  74. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/statements/schema.py +0 -0
  75. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/stresses.py +0 -0
  76. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/stressing/__init__.py +0 -0
  77. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/stressing/finder_parser.py +0 -0
  78. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/stressing/generator_parser.py +0 -0
  79. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/tasks.py +0 -0
  80. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/testcase_extractors.py +0 -0
  81. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/testcases/__init__.py +0 -0
  82. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/testcases/main.py +0 -0
  83. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/__init__.py +0 -0
  84. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/captured_log.py +0 -0
  85. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/__init__.py +0 -0
  86. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/build.py +0 -0
  87. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/command.py +0 -0
  88. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/error.py +0 -0
  89. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/run.py +0 -0
  90. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/run_explorer.py +0 -0
  91. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/run_test_explorer.py +0 -0
  92. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/selector.py +0 -0
  93. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/screens/test_explorer.py +0 -0
  94. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/utils/__init__.py +0 -0
  95. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/utils/run_ui.py +0 -0
  96. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/__init__.py +0 -0
  97. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/file_log.py +0 -0
  98. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/interaction_box.py +0 -0
  99. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/rich_log_box.py +0 -0
  100. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/test_output_box.py +0 -0
  101. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/ui/widgets/two_sided_test_output_box.py +0 -0
  102. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/validators.py +0 -0
  103. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/box/validators_test.py +0 -0
  104. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/checker.py +0 -0
  105. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/clone.py +0 -0
  106. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/config.py +0 -0
  107. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/conftest.py +0 -0
  108. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/console.py +0 -0
  109. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/create.py +0 -0
  110. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/edit.py +0 -0
  111. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/__init__.py +0 -0
  112. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/caching.py +0 -0
  113. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/conftest.py +0 -0
  114. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/__init__.py +0 -0
  115. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/cacher.py +0 -0
  116. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/digester.py +0 -0
  117. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/sandbox.py +0 -0
  118. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/sandboxes/__init__.py +0 -0
  119. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/sandboxes/isolate.py +0 -0
  120. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/sandboxes/stupid_sandbox.py +0 -0
  121. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/sandboxes/timeit.py +0 -0
  122. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/storage.py +0 -0
  123. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/test.py +0 -0
  124. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/judge/testiso.py +0 -0
  125. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/limits.py +0 -0
  126. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/processing_context.py +0 -0
  127. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/steps.py +0 -0
  128. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/steps_with_caching.py +0 -0
  129. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading/steps_with_caching_run_test.py +0 -0
  130. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/grading_utils.py +0 -0
  131. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/hydration.py +0 -0
  132. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/main.py +0 -0
  133. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/metadata.py +0 -0
  134. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/providers/__init__.py +0 -0
  135. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/providers/codeforces.py +0 -0
  136. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/providers/provider.py +0 -0
  137. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/checkers/boilerplate.cpp +0 -0
  138. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/default_config.json +0 -0
  139. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/default_setter_config.mac.yml +0 -0
  140. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/default_setter_config.yml +0 -0
  141. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/envs/default.rbx.yml +0 -0
  142. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/envs/isolate.rbx.yml +0 -0
  143. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/checker.sh +0 -0
  144. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compare.sh +0 -0
  145. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/c +0 -0
  146. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/cc +0 -0
  147. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/cpp +0 -0
  148. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/java +0 -0
  149. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/kt +0 -0
  150. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/pas +0 -0
  151. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/py2 +0 -0
  152. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/compile/py3 +0 -0
  153. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/c +0 -0
  154. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/cc +0 -0
  155. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/cpp +0 -0
  156. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/java +0 -0
  157. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/kt +0 -0
  158. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/py2 +0 -0
  159. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactive/py3 +0 -0
  160. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/interactor_compile.sh +0 -0
  161. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/bkp +0 -0
  162. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/c +0 -0
  163. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/cc +0 -0
  164. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/cpp +0 -0
  165. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/java +0 -0
  166. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/kt +0 -0
  167. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/py2 +0 -0
  168. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/boca/run/py3 +0 -0
  169. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/c/compile.sh +0 -0
  170. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/c/prep.sh +0 -0
  171. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/c/run.sh +0 -0
  172. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/compare.sh +0 -0
  173. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/cpp/compile.sh +0 -0
  174. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/cpp/prep.sh +0 -0
  175. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/cpp/run.sh +0 -0
  176. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/interactor_prep.sh +0 -0
  177. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/interactor_run.sh +0 -0
  178. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/java/compile.sh +0 -0
  179. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/java/prep.sh +0 -0
  180. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/java/run.sh +0 -0
  181. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py2/compile.sh +0 -0
  182. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py2/prep.sh +0 -0
  183. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py2/run.sh +0 -0
  184. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py3/compile.sh +0 -0
  185. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py3/prep.sh +0 -0
  186. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/packagers/moj/scripts/py3/run.sh +0 -0
  187. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/contest/contest.rbx.yml +0 -0
  188. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/contest/statement/contest.rbx.tex +0 -0
  189. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/contest/statement/olymp.sty +0 -0
  190. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/contest/statement/template.rbx.tex +0 -0
  191. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/preset.rbx.yml +0 -0
  192. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/.gitignore +0 -0
  193. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/gen.cpp +0 -0
  194. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/problem.rbx.yml +0 -0
  195. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/random.py +0 -0
  196. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/random.txt +0 -0
  197. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/sols/main.cpp +0 -0
  198. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/sols/slow.cpp +0 -0
  199. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/sols/wa.cpp +0 -0
  200. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/statement/olymp.sty +0 -0
  201. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/statement/projecao.png +0 -0
  202. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/statement/statement.rbx.tex +0 -0
  203. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/statement/template.rbx.tex +0 -0
  204. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/tests/samples/000.in +0 -0
  205. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/tests/samples/001.in +0 -0
  206. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/validator.cpp +0 -0
  207. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/presets/default/problem/wcmp.cpp +0 -0
  208. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/templates/rbx.h +0 -0
  209. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/resources/templates/template.cpp +0 -0
  210. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/run.py +0 -0
  211. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/schema.py +0 -0
  212. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/submit.py +0 -0
  213. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/submitors/__init__.py +0 -0
  214. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/submitors/codeforces.py +0 -0
  215. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/submitors/submitor.py +0 -0
  216. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/test.py +0 -0
  217. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/testcase.py +0 -0
  218. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/testcase_rendering.py +0 -0
  219. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/testing_utils.py +0 -0
  220. {rbx_cp-0.5.64 → rbx_cp-0.5.66}/rbx/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.5.64
3
+ Version: 0.5.66
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9,<4.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "rbx.cp"
3
- version = "0.5.64"
3
+ version = "0.5.66"
4
4
  description = ""
5
5
  packages = [
6
6
  {include = "rbx"}
@@ -3,7 +3,7 @@ import shlex
3
3
  import shutil
4
4
  import sys
5
5
  import tempfile
6
- from typing import Annotated, Optional
6
+ from typing import Annotated, List, Optional
7
7
 
8
8
  import rich
9
9
  import rich.prompt
@@ -136,6 +136,13 @@ def ui():
136
136
  ui_pkg.start()
137
137
 
138
138
 
139
+ @app.command('diff', hidden=True)
140
+ def diff(path1: pathlib.Path, path2: pathlib.Path):
141
+ from rbx.box.ui import main as ui_pkg
142
+
143
+ ui_pkg.start_differ(path1, path2)
144
+
145
+
139
146
  @app.command('serve', hidden=True)
140
147
  def serve():
141
148
  from textual_serve.server import Server
@@ -178,10 +185,10 @@ async def build(verification: environment.VerificationParam):
178
185
  @syncer.sync
179
186
  async def run(
180
187
  verification: environment.VerificationParam,
181
- solution: Annotated[
182
- Optional[str],
188
+ solutions: Annotated[
189
+ Optional[List[str]],
183
190
  typer.Argument(
184
- help='Path to solution to run. If not specified, will run all solutions.'
191
+ help='Path to solutions to run. If not specified, will run all solutions.'
185
192
  ),
186
193
  ] = None,
187
194
  outcome: Optional[str] = typer.Option(
@@ -235,11 +242,16 @@ async def run(
235
242
  str(solution.path)
236
243
  for solution in get_matching_solutions(ExpectedOutcome(outcome))
237
244
  }
238
- if solution:
239
- tracked_solutions = {solution}
245
+ if solutions:
246
+ tracked_solutions = set(solutions)
240
247
 
241
248
  if choice:
242
- tracked_solutions = set(await pick_solutions(tracked_solutions))
249
+ tracked_solutions = set(
250
+ await pick_solutions(
251
+ tracked_solutions,
252
+ extra_solutions=solutions,
253
+ )
254
+ )
243
255
  if not tracked_solutions:
244
256
  console.console.print('[error]No solutions selected. Exiting.[/error]')
245
257
  raise typer.Exit(1)
@@ -395,10 +407,10 @@ async def time(
395
407
  @syncer.sync
396
408
  async def irun(
397
409
  verification: environment.VerificationParam,
398
- solution: Annotated[
399
- Optional[str],
410
+ solutions: Annotated[
411
+ Optional[List[str]],
400
412
  typer.Argument(
401
- help='Path to solution to run. If not specified, will run all solutions.'
413
+ help='Path to solutions to run. If not specified, will run all solutions.'
402
414
  ),
403
415
  ] = None,
404
416
  outcome: Optional[str] = typer.Option(
@@ -466,11 +478,16 @@ async def irun(
466
478
  str(solution.path)
467
479
  for solution in get_matching_solutions(ExpectedOutcome(outcome))
468
480
  }
469
- if solution:
470
- tracked_solutions = {solution}
481
+ if solutions:
482
+ tracked_solutions = set(solutions)
471
483
 
472
484
  if choice:
473
- tracked_solutions = set(await pick_solutions(tracked_solutions))
485
+ tracked_solutions = set(
486
+ await pick_solutions(
487
+ tracked_solutions,
488
+ extra_solutions=solutions,
489
+ )
490
+ )
474
491
  if not tracked_solutions:
475
492
  console.console.print('[error]No solutions selected. Exiting.[/error]')
476
493
  raise typer.Exit(1)
@@ -20,7 +20,11 @@ def href(url: os.PathLike[str], text: Optional[str] = None, style: str = 'item')
20
20
 
21
21
  if isinstance(url, pathlib.Path):
22
22
  url = url.resolve()
23
- return f'[{style}][link={url}]{text}[/link][/{style}]'
23
+
24
+ url_str = str(url)
25
+ if pathlib.Path(url_str).exists():
26
+ url_str = f'file://{url_str}'
27
+ return f'[{style}][link={url_str}]{text}[/link][/{style}]'
24
28
 
25
29
 
26
30
  def get_formatted_memory(memory_in_bytes: int, mib_decimal_places: int = 0) -> str:
@@ -150,6 +150,19 @@ def get_problem_cache_dir(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
150
150
  return cache_dir
151
151
 
152
152
 
153
+ @functools.cache
154
+ def get_problem_remote_dir(
155
+ platform: Optional[str] = None, root: pathlib.Path = pathlib.Path()
156
+ ) -> pathlib.Path:
157
+ remote_dir = get_problem_cache_dir(root) / '.remote'
158
+ remote_dir.mkdir(parents=True, exist_ok=True)
159
+
160
+ if platform is not None:
161
+ remote_dir = remote_dir / platform
162
+ remote_dir.mkdir(parents=True, exist_ok=True)
163
+ return remote_dir
164
+
165
+
153
166
  @functools.cache
154
167
  def get_problem_storage_dir(root: pathlib.Path = pathlib.Path()) -> pathlib.Path:
155
168
  storage_dir = get_problem_cache_dir(root) / '.storage'
@@ -4,6 +4,7 @@ import hashlib
4
4
  import os
5
5
  import pathlib
6
6
  import re
7
+ import shutil
7
8
  import typing
8
9
  from typing import Any, NoReturn, Optional, Tuple
9
10
 
@@ -248,13 +249,50 @@ class BocaUploader:
248
249
  '[error]Persistent error while uploading problem to BOCA website.[/error]'
249
250
  )
250
251
  console.console.print(
251
- '[warning]This might be caused by PHP max upload size limit (which usually defaults to 2MBF).[/warning]'
252
+ '[warning]This might be caused by PHP max upload size limit (which usually defaults to 2MB).[/warning]'
252
253
  )
253
254
  console.console.print(
254
255
  '[warning]Check [item]https://www.php.net/manual/en/ini.core.php#ini.sect.file-uploads[/item] for more information.[/warning]'
255
256
  )
256
257
  raise typer.Exit(1)
257
258
 
259
+ def download_run(self, run_number: int, site_number: int, into_dir: pathlib.Path):
260
+ url = f'{self.base_url}/admin/runedit.php?runnumber={run_number}&runsitenumber={site_number}'
261
+ _, html = self.open(
262
+ url,
263
+ error_msg=f'Error while downloading BOCA run [item]{run_number}-{site_number}[/item]',
264
+ )
265
+
266
+ soup = BeautifulSoup(html, 'html.parser')
267
+ rows = soup.select('tr')
268
+
269
+ href: Optional[str] = None
270
+ filename: Optional[pathlib.Path] = None
271
+
272
+ for row in rows:
273
+ row_text = row.select('td')[0].text.strip().lower()
274
+ if row_text != "team's code:":
275
+ continue
276
+ link_col = row.select_one('td:nth-of-type(2) a:nth-of-type(1)')
277
+ if link_col is None:
278
+ continue
279
+ href = str(link_col.attrs['href'])
280
+ filename = pathlib.Path(link_col.text.strip())
281
+ break
282
+
283
+ if href is None or filename is None:
284
+ self.raw_error(
285
+ "Error while downloading run:\nNo link to team's code found."
286
+ )
287
+
288
+ link = self.br.find_link(url=href)
289
+ tmp_file, _ = self.br.retrieve(link.absolute_url)
290
+ if tmp_file is None:
291
+ self.raw_error('Error while downloading run:\nDownloaded file is None.')
292
+ final_path = into_dir / filename.with_stem(f'{run_number}-{site_number}')
293
+ shutil.move(tmp_file, final_path)
294
+ return final_path
295
+
258
296
 
259
297
  @functools.lru_cache
260
298
  def get_boca_uploader(
@@ -159,12 +159,10 @@ def compile_solutions(
159
159
 
160
160
  compiled_solutions = {}
161
161
 
162
- for solution in pkg.solutions:
163
- if (
164
- tracked_solutions is not None
165
- and str(solution.path) not in tracked_solutions
166
- ):
167
- continue
162
+ if tracked_solutions is None:
163
+ tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
164
+
165
+ for solution in expand_solutions(list(tracked_solutions)):
168
166
  if progress:
169
167
  progress.update(f'Compiling solution {href(solution.path)}...')
170
168
  try:
@@ -254,11 +252,7 @@ def _get_solutions_for_skeleton(
254
252
  if verification.value >= VerificationLevel.ALL_SOLUTIONS.value or is_fast(sol)
255
253
  ]
256
254
  if tracked_solutions is not None:
257
- solutions = [
258
- solution
259
- for solution in solutions
260
- if str(solution.path) in tracked_solutions
261
- ]
255
+ solutions = expand_solutions(list(tracked_solutions))
262
256
  return solutions
263
257
 
264
258
 
@@ -726,20 +720,105 @@ def _get_solution_repr(sol: Solution) -> List[Tuple[str, str]]:
726
720
  ]
727
721
 
728
722
 
729
- async def pick_solutions(tracked_solutions: Optional[Set[str]]) -> List[str]:
723
+ # TODO: refactor this function
724
+ def _expand_remote_sol(sol: str) -> Optional[str]:
725
+ from rbx.box.packaging.boca import upload as boca_upload
726
+
727
+ # Only BOCA supported for now.
728
+ assert sol.startswith('@')
729
+ sol = sol[1:]
730
+ if '-' in sol:
731
+ run_number, site_number = sol.split('-', 1)
732
+ run_number = int(run_number)
733
+ site_number = int(site_number)
734
+ else:
735
+ run_number = int(sol)
736
+ site_number = 1
737
+
738
+ expected_glob = (
739
+ package.get_problem_remote_dir('boca') / f'{run_number}-{site_number}.*'
740
+ )
741
+ expected_glob = expected_glob.relative_to(pathlib.Path.cwd())
742
+ for path in pathlib.Path.cwd().glob(str(expected_glob)):
743
+ if path.is_file():
744
+ relpath = path.resolve().relative_to(pathlib.Path.cwd())
745
+ console.console.print(f'Retrieving {href(relpath)} from cache...')
746
+ return str(relpath)
747
+
748
+ boca_uploader = boca_upload.get_boca_uploader()
749
+ boca_uploader.login()
750
+ sol_path = boca_uploader.download_run(
751
+ run_number, site_number, package.get_problem_remote_dir('boca')
752
+ )
753
+ console.console.print(f'Downloaded {href(sol_path)} from BOCA...')
754
+ return str(sol_path.resolve().relative_to(pathlib.Path.cwd()))
755
+
756
+
757
+ def _expand_remote_sols(sols: List[str]) -> List[str]:
758
+ res = []
759
+ for sol in sols:
760
+ if not sol.startswith('@'):
761
+ res.append(sol)
762
+ continue
763
+ expanded = _expand_remote_sol(sol)
764
+ if expanded is None:
765
+ continue
766
+ res.append(expanded)
767
+ return res
768
+
769
+
770
+ def expand_solutions(sols: List[str]) -> List[Solution]:
730
771
  pkg = package.find_problem_package_or_die()
731
- if tracked_solutions is None:
732
- tracked_solutions = set(str(sol.path) for sol in pkg.solutions)
772
+ seen_sols = {str(sol.path): sol for sol in pkg.solutions}
773
+
774
+ # Download remote sols.
775
+ sols = _expand_remote_sols(sols)
776
+
777
+ # Ensure sols exist.
778
+ sols = [sol for sol in sols if pathlib.Path(sol).is_file()]
779
+
780
+ res = []
781
+ for sol in sols:
782
+ if sol in seen_sols:
783
+ res.append(seen_sols[sol])
784
+ else:
785
+ res.append(
786
+ Solution(path=pathlib.Path(sol), outcome=ExpectedOutcome.ACCEPTED)
787
+ )
788
+ return res
789
+
733
790
 
791
+ async def pick_solutions(
792
+ tracked_solutions: Optional[Set[str]],
793
+ extra_solutions: Optional[List[str]] = None,
794
+ ) -> List[str]:
795
+ pkg = package.find_problem_package_or_die()
734
796
  # Store in a separate list to maintain order with the package declaration.
735
797
  import questionary
736
798
 
737
799
  choices = [
738
- questionary.Choice(title=_get_solution_repr(sol), value=str(sol.path))
800
+ questionary.Choice(
801
+ title=_get_solution_repr(sol),
802
+ value=str(sol.path),
803
+ checked=tracked_solutions is None or str(sol.path) in tracked_solutions,
804
+ )
739
805
  for sol in pkg.solutions
740
- if str(sol.path) in tracked_solutions
741
806
  ]
742
807
 
808
+ seen_sols = set(str(sol.path) for sol in pkg.solutions)
809
+
810
+ if extra_solutions is not None:
811
+ # Add only new solutions.
812
+ choices.extend(
813
+ questionary.Choice(
814
+ title=_get_solution_repr(sol),
815
+ value=str(sol.path),
816
+ checked=True,
817
+ )
818
+ for sol in expand_solutions(extra_solutions)
819
+ if str(sol.path) not in seen_sols
820
+ )
821
+
743
822
  picked = await questionary.checkbox('Select solutions', choices=choices).ask_async()
744
823
  if picked is None:
745
824
  raise typer.Abort()
@@ -172,6 +172,7 @@ def parse_interaction(file: pathlib.Path) -> TestcaseInteraction:
172
172
  )
173
173
  raise typer.Exit(1) from None
174
174
 
175
+ # Crop file.
175
176
  rest = f.read()
176
177
  start = 0
177
178
 
@@ -202,13 +203,17 @@ def parse_interaction(file: pathlib.Path) -> TestcaseInteraction:
202
203
  nxt_start, _ = nxt
203
204
  return (pipe, (prefix_end, nxt_start))
204
205
 
205
- while True:
206
+ # TODO: optimize
207
+ blocks = 0
208
+ MAX_BLOCKS = 1024
209
+ while blocks < MAX_BLOCKS:
206
210
  block = _find_next_block()
207
211
  if block is None:
208
212
  break
209
213
  pipe, (st, nd) = block
210
214
  entries.append(TestcaseInteractionEntry(data=rest[st:nd], pipe=pipe))
211
215
  start = nd
216
+ blocks += 1
212
217
 
213
218
  return TestcaseInteraction(
214
219
  prefixes=(interactor_prefix, solution_prefix),
@@ -21,6 +21,20 @@ OptionList {
21
21
  border: solid $accent;
22
22
  }
23
23
 
24
+ DiffBox {
25
+ border: solid $accent;
26
+ height: 1fr;
27
+ width: 1fr;
28
+ Markdown {
29
+ height: 1fr;
30
+ width: 1fr;
31
+
32
+ .code_inline {
33
+ background: $background;
34
+ }
35
+ }
36
+ }
37
+
24
38
  Button {
25
39
  width: 1fr;
26
40
  background: $accent;
@@ -1,3 +1,4 @@
1
+ import pathlib
1
2
  from typing import Type
2
3
 
3
4
  from textual.app import App, ComposeResult
@@ -5,6 +6,7 @@ from textual.containers import Center
5
6
  from textual.screen import Screen
6
7
  from textual.widgets import Footer, Header, OptionList
7
8
 
9
+ from rbx.box.ui.screens.differ import DifferScreen
8
10
  from rbx.box.ui.screens.run_explorer import RunExplorerScreen
9
11
  from rbx.box.ui.screens.test_explorer import TestExplorerScreen
10
12
 
@@ -35,6 +37,25 @@ class rbxApp(App):
35
37
  self.push_screen(screen_cls())
36
38
 
37
39
 
40
+ class rbxDifferApp(App):
41
+ TITLE = 'rbx differ'
42
+ CSS_PATH = 'css/app.tcss'
43
+ BINDINGS = [('q', 'quit', 'Quit')]
44
+
45
+ def __init__(self, path1: pathlib.Path, path2: pathlib.Path):
46
+ super().__init__()
47
+ self.path1 = path1
48
+ self.path2 = path2
49
+
50
+ def on_mount(self):
51
+ self.push_screen(DifferScreen(self.path1, self.path2))
52
+
53
+
38
54
  def start():
39
55
  app = rbxApp()
40
56
  app.run()
57
+
58
+
59
+ def start_differ(path1: pathlib.Path, path2: pathlib.Path):
60
+ app = rbxDifferApp(path1, path2)
61
+ app.run()
@@ -0,0 +1,29 @@
1
+ import pathlib
2
+
3
+ from textual.app import ComposeResult
4
+ from textual.containers import Vertical
5
+ from textual.screen import Screen
6
+ from textual.widgets import Footer, Header
7
+
8
+ from rbx.box.ui.widgets.diff_box import DiffBox
9
+
10
+
11
+ class DifferScreen(Screen):
12
+ BINDINGS = [
13
+ ('q', 'quit', 'Quit'),
14
+ ]
15
+
16
+ def __init__(self, path1: pathlib.Path, path2: pathlib.Path):
17
+ super().__init__()
18
+ self.path1 = path1
19
+ self.path2 = path2
20
+
21
+ def compose(self) -> ComposeResult:
22
+ yield Header()
23
+ yield Footer()
24
+ with Vertical():
25
+ yield DiffBox()
26
+
27
+ def on_mount(self):
28
+ diff = self.query_one(DiffBox)
29
+ diff.paths = (self.path1, self.path2)
@@ -0,0 +1,38 @@
1
+ import difflib
2
+ import pathlib
3
+ from typing import Optional, Tuple
4
+
5
+ from rich.markdown import Markdown
6
+ from textual.app import ComposeResult
7
+ from textual.reactive import reactive
8
+ from textual.widget import Widget
9
+ from textual.widgets import RichLog
10
+
11
+
12
+ def compute_diff(file1: pathlib.Path, file2: pathlib.Path) -> str:
13
+ lines1 = file1.read_text().splitlines(keepends=True)
14
+ lines2 = file2.read_text().splitlines(keepends=True)
15
+ return ''.join(difflib.ndiff(lines1, lines2))
16
+
17
+
18
+ class DiffBox(Widget, can_focus=False):
19
+ paths: reactive[Optional[Tuple[pathlib.Path, pathlib.Path]]] = reactive(None)
20
+
21
+ def __init__(self):
22
+ super().__init__()
23
+
24
+ def compose(self) -> ComposeResult:
25
+ md = RichLog()
26
+ md.border_title = 'Differ'
27
+ yield md
28
+
29
+ async def watch_paths(self, paths: Optional[Tuple[pathlib.Path, pathlib.Path]]):
30
+ log = self.query_one(RichLog)
31
+ log.clear()
32
+ if paths is None:
33
+ return
34
+ file1, file2 = paths
35
+ md = Markdown(
36
+ f'```diff\n{compute_diff(file1, file2)}\n```', code_theme='monokai'
37
+ )
38
+ log.write(md)
@@ -148,8 +148,6 @@ async def run_validator_unit_tests(progress: StatusProgress):
148
148
 
149
149
  async def run_checker_unit_tests(progress: StatusProgress):
150
150
  pkg = package.find_problem_package_or_die()
151
- if not pkg.unitTests.checker:
152
- return
153
151
 
154
152
  if not package.get_checker():
155
153
  console.console.print(
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
File without changes
File without changes
File without changes
File without changes
File without changes