rbx.cp 0.5.62__py3-none-any.whl → 0.5.63__py3-none-any.whl

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.
rbx/box/solutions.py CHANGED
@@ -16,7 +16,7 @@ import typer
16
16
  from pydantic import BaseModel
17
17
 
18
18
  from rbx import console, utils
19
- from rbx.box import checkers, environment, package
19
+ from rbx.box import checkers, environment, package, state
20
20
  from rbx.box.code import (
21
21
  SanitizationLevel,
22
22
  compile_item,
@@ -87,6 +87,7 @@ class SolutionReportSkeleton(BaseModel):
87
87
  groups: List[GroupSkeleton]
88
88
  limits: Dict[str, Limits]
89
89
  verification: VerificationLevel
90
+ capture_pipes: bool = False
90
91
 
91
92
  def find_group_skeleton(self, group_name: str) -> Optional[GroupSkeleton]:
92
93
  groups = [group for group in self.groups if group.name == group_name]
@@ -303,6 +304,7 @@ def _get_report_skeleton(
303
304
  limits=limits,
304
305
  entries=entries,
305
306
  verification=verification,
307
+ capture_pipes=state.STATE.debug_logs,
306
308
  )
307
309
 
308
310
  skeleton_file = runs_dir / 'skeleton.yml'
@@ -627,6 +629,7 @@ def _get_interactive_skeleton(
627
629
  limits=limits,
628
630
  entries=[],
629
631
  verification=verification,
632
+ capture_pipes=True,
630
633
  )
631
634
 
632
635
  skeleton_file = irun_dir / 'skeleton.yml'
@@ -7,6 +7,7 @@ from textual.screen import Screen
7
7
  from textual.widgets import Footer, Header, Label, ListItem, ListView
8
8
 
9
9
  from rbx.box import package
10
+ from rbx.box.schema import TaskType
10
11
  from rbx.box.solutions import SolutionReportSkeleton, SolutionSkeleton
11
12
  from rbx.box.testcase_extractors import (
12
13
  GenerationTestcaseEntry,
@@ -64,6 +65,9 @@ class RunTestExplorerScreen(Screen):
64
65
  self.query_one('#test-list').border_title = 'Tests'
65
66
  self.query_one('#test-input').border_title = 'Input'
66
67
 
68
+ # Ensure the output is show, even for interactive tests
69
+ self.action_show_output()
70
+
67
71
  await self._update_tests()
68
72
 
69
73
  def _get_rendering_data(
@@ -118,8 +122,15 @@ class RunTestExplorerScreen(Screen):
118
122
  def has_diffable_solution(self) -> bool:
119
123
  return self.diff_solution is not None or package.get_main_solution() is not None
120
124
 
125
+ def should_show_interaction(self) -> bool:
126
+ pkg = package.find_problem_package_or_die()
127
+ return pkg.type == TaskType.COMMUNICATION and self.skeleton.capture_pipes
128
+
121
129
  def action_show_output(self):
122
- self.query_one('#test-output', TwoSidedTestBoxWidget).show_output()
130
+ if self.should_show_interaction():
131
+ self.query_one('#test-output', TwoSidedTestBoxWidget).show_interaction()
132
+ else:
133
+ self.query_one('#test-output', TwoSidedTestBoxWidget).show_output()
123
134
 
124
135
  def action_show_stderr(self):
125
136
  self.query_one('#test-output', TwoSidedTestBoxWidget).show_stderr()
@@ -5,6 +5,8 @@ from textual.containers import Horizontal, Vertical
5
5
  from textual.screen import Screen
6
6
  from textual.widgets import Footer, Header, Label, ListItem, ListView, RichLog
7
7
 
8
+ from rbx.box import package
9
+ from rbx.box.schema import TaskType
8
10
  from rbx.box.testcase_extractors import (
9
11
  GenerationTestcaseEntry,
10
12
  extract_generation_testcases_from_groups,
@@ -42,6 +44,9 @@ class TestExplorerScreen(Screen):
42
44
  self.query_one('#test-list').border_title = 'Tests'
43
45
  self.query_one('#test-input').border_title = 'Input'
44
46
 
47
+ # Ensure either output or interaction is visible.
48
+ self.action_show_output()
49
+
45
50
  metadata = self.query_one('#test-metadata', RichLogBox)
46
51
  metadata.display = False
47
52
  metadata.border_title = 'Metadata'
@@ -67,6 +72,7 @@ class TestExplorerScreen(Screen):
67
72
  entry = self._entries[index]
68
73
  input.path = entry.metadata.copied_to.inputPath
69
74
 
75
+ assert entry.metadata.copied_to.outputPath is not None
70
76
  output.data = TestcaseRenderingData.from_one_path(
71
77
  entry.metadata.copied_to.outputPath
72
78
  )
@@ -105,8 +111,15 @@ class TestExplorerScreen(Screen):
105
111
  [ListItem(Label(name)) for name in test_names]
106
112
  )
107
113
 
114
+ def is_interactive(self) -> bool:
115
+ pkg = package.find_problem_package_or_die()
116
+ return pkg.type == TaskType.COMMUNICATION
117
+
108
118
  def action_show_output(self):
109
- self.query_one('#test-output', TestBoxWidget).show_output()
119
+ if self.is_interactive():
120
+ self.query_one('#test-output', TestBoxWidget).show_interaction()
121
+ else:
122
+ self.query_one('#test-output', TestBoxWidget).show_output()
110
123
 
111
124
  def action_show_stderr(self):
112
125
  self.query_one('#test-output', TestBoxWidget).show_stderr()
@@ -0,0 +1,59 @@
1
+ import asyncio
2
+ import pathlib
3
+ from typing import Optional
4
+
5
+ import rich.text
6
+ from textual import work
7
+ from textual.reactive import reactive
8
+
9
+ from rbx.box import testcase_utils
10
+ from rbx.box.ui.widgets.rich_log_box import RichLogBox
11
+
12
+ BATCH_SIZE = 1024
13
+
14
+
15
+ class InteractionBox(RichLogBox):
16
+ DEFAULT_CSS = """
17
+ InteractionBox {
18
+ border: solid $accent;
19
+ height: 1fr;
20
+ width: 1fr;
21
+ }
22
+ """
23
+
24
+ path: reactive[Optional[pathlib.Path]] = reactive(None)
25
+
26
+ def on_mount(self):
27
+ super().on_mount()
28
+ self.auto_scroll = False
29
+ self.can_focus = False
30
+
31
+ @work(exclusive=True)
32
+ async def _load_file(self, path: pathlib.Path):
33
+ self.clear()
34
+ path_str = str(path.relative_to(pathlib.Path.cwd()))
35
+ self.border_subtitle = f'{path_str} (loading...)'
36
+
37
+ interaction = await asyncio.to_thread(testcase_utils.parse_interaction, path)
38
+
39
+ for entry in interaction.entries:
40
+ if entry.pipe == 0:
41
+ self.write(rich.text.Text(entry.data.rstrip(), style='green'))
42
+ else:
43
+ self.write(rich.text.Text(entry.data.rstrip()))
44
+
45
+ self.border_subtitle = path_str
46
+
47
+ async def watch_path(self, path: Optional[pathlib.Path]):
48
+ self.clear()
49
+
50
+ if path is None:
51
+ self.border_subtitle = '(no file selected)'
52
+ return
53
+
54
+ if not path.is_file():
55
+ path_str = str(path.relative_to(pathlib.Path.cwd()))
56
+ self.border_subtitle = f'{path_str} (does not exist)'
57
+ return
58
+
59
+ self._load_file(path)
@@ -9,6 +9,7 @@ from textual.widget import Widget
9
9
  from textual.widgets import ContentSwitcher
10
10
 
11
11
  from rbx.box.ui.widgets.file_log import FileLog
12
+ from rbx.box.ui.widgets.interaction_box import InteractionBox
12
13
  from rbx.box.ui.widgets.rich_log_box import RichLogBox
13
14
 
14
15
 
@@ -17,6 +18,7 @@ class TestcaseRenderingData:
17
18
  input_path: Optional[pathlib.Path] = None
18
19
  output_path: Optional[pathlib.Path] = None
19
20
  stderr_path: Optional[pathlib.Path] = None
21
+ interaction_path: Optional[pathlib.Path] = None
20
22
  log_path: Optional[pathlib.Path] = None
21
23
  rich_content: Optional[str] = None
22
24
 
@@ -27,6 +29,7 @@ class TestcaseRenderingData:
27
29
  output_path=path.with_suffix('.out'),
28
30
  stderr_path=path.with_suffix('.err'),
29
31
  log_path=path.with_suffix('.log'),
32
+ interaction_path=path.with_suffix('.pio'),
30
33
  )
31
34
 
32
35
 
@@ -41,12 +44,14 @@ class TestBoxWidget(Widget, can_focus=False):
41
44
  output: FileLog
42
45
  stderr: FileLog
43
46
  log: FileLog
47
+ interaction: FileLog
44
48
 
45
49
  def logs(self) -> Logs:
46
50
  return self.Logs(
47
51
  output=self.query_one('#test-box-output', FileLog),
48
52
  stderr=self.query_one('#test-box-stderr', FileLog),
49
53
  log=self.query_one('#test-box-log', FileLog),
54
+ interaction=self.query_one('#test-box-interaction', InteractionBox),
50
55
  )
51
56
 
52
57
  def compose(self) -> ComposeResult:
@@ -55,6 +60,7 @@ class TestBoxWidget(Widget, can_focus=False):
55
60
  yield FileLog(id='test-box-output')
56
61
  yield FileLog(id='test-box-stderr')
57
62
  yield FileLog(id='test-box-log')
63
+ yield InteractionBox(id='test-box-interaction')
58
64
  yield RichLogBox(id='test-box-metadata')
59
65
 
60
66
  def on_mount(self):
@@ -62,7 +68,7 @@ class TestBoxWidget(Widget, can_focus=False):
62
68
  logs.output.border_title = 'Output'
63
69
  logs.stderr.border_title = 'Stderr'
64
70
  logs.log.border_title = 'Log'
65
-
71
+ logs.interaction.border_title = 'Interaction'
66
72
  metadata = self.query_one('#test-box-metadata', RichLogBox)
67
73
  metadata.display = False
68
74
  metadata.border_title = 'Metadata'
@@ -78,7 +84,7 @@ class TestBoxWidget(Widget, can_focus=False):
78
84
  logs.output.path = data.output_path
79
85
  logs.stderr.path = data.stderr_path
80
86
  logs.log.path = data.log_path
81
-
87
+ logs.interaction.path = data.interaction_path
82
88
  metadata = self.query_one('#test-box-metadata', RichLogBox)
83
89
  metadata.clear()
84
90
  if data.rich_content is not None:
@@ -98,6 +104,9 @@ class TestBoxWidget(Widget, can_focus=False):
98
104
  def show_log(self):
99
105
  self.query_one(ContentSwitcher).current = 'test-box-log'
100
106
 
107
+ def show_interaction(self):
108
+ self.query_one(ContentSwitcher).current = 'test-box-interaction'
109
+
101
110
  def toggle_metadata(self):
102
111
  metadata = self.query_one('#test-box-metadata', RichLogBox)
103
112
  metadata.display = not metadata.display
@@ -51,6 +51,10 @@ class TwoSidedTestBoxWidget(Widget, can_focus=False):
51
51
  self.query_one('#test-box-1', TestBoxWidget).show_log()
52
52
  self.query_one('#test-box-2', TestBoxWidget).show_log()
53
53
 
54
+ def show_interaction(self):
55
+ self.query_one('#test-box-1', TestBoxWidget).show_interaction()
56
+ self.query_one('#test-box-2', TestBoxWidget).show_interaction()
57
+
54
58
  def toggle_metadata(self):
55
59
  self.query_one('#test-box-1', TestBoxWidget).toggle_metadata()
56
60
  self.query_one('#test-box-2', TestBoxWidget).toggle_metadata()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rbx.cp
3
- Version: 0.5.62
3
+ Version: 0.5.63
4
4
  Summary:
5
5
  Author: Roberto Sales
6
6
  Requires-Python: >=3.9,<4.0
@@ -52,7 +52,7 @@ rbx/box/retries.py,sha256=cZcNYLVHFDbhbeqMzxITgo8SYY8qzyxm0tIYcmWl1Ek,4877
52
52
  rbx/box/sanitizers/warning_stack.py,sha256=RI97_GJgdjTKIXY_r0EKp5h0qQQSDSdNDh5K7zINrqs,2861
53
53
  rbx/box/schema.py,sha256=y736-wZdGw56T6eDC_m7NAm2XRUdauBXJRQkQO79fpc,16264
54
54
  rbx/box/setter_config.py,sha256=s53talhwM6FTGDCcBhY7IlZ6_6mJ3PMp6V4kTtaSs50,4262
55
- rbx/box/solutions.py,sha256=rQVkBvNqVrwFgmfgzBIvPUhWu-GDiRRbuXy_zvZKfpA,48965
55
+ rbx/box/solutions.py,sha256=tMG72QbdexEsfiw70Ryn6hcnXiiNbHIKMAZX0_6uYEY,49078
56
56
  rbx/box/solutions_test.py,sha256=PX1TQoRzNd9mi1SGsG7WFrpqFgNrNX5Kwt0mkwFdoOA,1749
57
57
  rbx/box/state.py,sha256=MMf3DvfQji0jKEliCHct2Tpp_0epL1tvP8HbHNArQIc,166
58
58
  rbx/box/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -81,16 +81,17 @@ rbx/box/ui/screens/command.py,sha256=s29xHhvdQhgr9_E7LjggoKmSw3UuQQrJV3UQNnK97Mk
81
81
  rbx/box/ui/screens/error.py,sha256=k6Rs5maR_APKepMtPcDMSXo6BDKrP-pAnFFJgqmXDKE,496
82
82
  rbx/box/ui/screens/run.py,sha256=eKIfFRi-VbqKDxlhZDuOEdVl91XCndnmNIZyj_LMV0c,6028
83
83
  rbx/box/ui/screens/run_explorer.py,sha256=Q6VUhiqBaKr70bqA7pV5qKIQamLqUBCtxZnX2tDkap0,2651
84
- rbx/box/ui/screens/run_test_explorer.py,sha256=GeCCJblkqJtDGycxsa6gy9luhhWT-_sdSFVZHXV2FpY,5538
84
+ rbx/box/ui/screens/run_test_explorer.py,sha256=rnO2uIeG_7_vZ_HrRwtNSrDkcyo6RU5hITtcG2HSt0w,6001
85
85
  rbx/box/ui/screens/selector.py,sha256=BmzyEVeBKD9IEUiEea-Y04HnNLzDYQJxAj7Jm-s-D3I,753
86
- rbx/box/ui/screens/test_explorer.py,sha256=pNLRkH_HDWDoPNDQO33vbTRX18B9RFZ6ZZ3vXbeKaj0,4105
86
+ rbx/box/ui/screens/test_explorer.py,sha256=p_MQmTvmNsB5j2E-CwN8G1mS756aWSMWGonkwMXym2I,4595
87
87
  rbx/box/ui/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  rbx/box/ui/utils/run_ui.py,sha256=457xNZteBTb82Rcg5Cr3AmgBIHLOmRrF4BKmCRyxq3Q,3113
89
89
  rbx/box/ui/widgets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
90
  rbx/box/ui/widgets/file_log.py,sha256=3wlrmkWR-EMibwlwXOJ5sGpTFwFEkRaGYo5fdmf8L3w,1704
91
+ rbx/box/ui/widgets/interaction_box.py,sha256=WDHmpO5ck21y6hNdUVxvJ81jEg9TVJh39hFJUc5-Dos,1572
91
92
  rbx/box/ui/widgets/rich_log_box.py,sha256=mF565c_Y3RYUZ_GJEFj5Eb86SFjsib31wE5qu1K0UBM,91
92
- rbx/box/ui/widgets/test_output_box.py,sha256=BAJo3Td4l2q829VzbJAhBsGVBeHA-b4CWqAzoraVD2c,3309
93
- rbx/box/ui/widgets/two_sided_test_output_box.py,sha256=8XpRj-83DGpFSi3Cog6JC-Y99K4F3f89QXKqpUfusqU,2112
93
+ rbx/box/ui/widgets/test_output_box.py,sha256=yqeAb-JFvVx1Q7w_qJbDMWQigyGy4ofGECDxQ7P0_2s,3864
94
+ rbx/box/ui/widgets/two_sided_test_output_box.py,sha256=L-ORiDwd6CP5DFpavrKGBaX0ZHkSoQqbJrGZ4BdFUWc,2289
94
95
  rbx/box/unit.py,sha256=PY96t8qnsHLsoJVanbDnrIx-s8Dada9Fj_v375MhvTw,6477
95
96
  rbx/box/validators.py,sha256=oqlNhw7jivbbH5l8g3xwihPRy76AM7MA3G4A8nyI_V0,10416
96
97
  rbx/box/validators_test.py,sha256=WY4Ho-wlsPHc0YNuz0KFVd6KQ9ouuiou3w5_zMOZ4Fs,362
@@ -211,8 +212,8 @@ rbx/testcase.py,sha256=yKOq3CAJZ1YTmInvnoIs0u1iJnRj_X85XiWbLI-p9d8,1951
211
212
  rbx/testcase_rendering.py,sha256=nfmv6dSEqd4aR3TsaODwkKGK6AXty_DDKtWf_ejiQpI,2084
212
213
  rbx/testing_utils.py,sha256=x_PqD8Zd2PkN91NxVHUnSTs044-1WK5KKtttKQBXpFs,2083
213
214
  rbx/utils.py,sha256=SfR844_i0ebRDMkmS_w1YdZiWPc6h2RGADygewlWRbA,4845
214
- rbx_cp-0.5.62.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
215
- rbx_cp-0.5.62.dist-info/METADATA,sha256=hZ61j3xaUIH0ixaZtbKLfvl_V5Kenl7CZjM84VDtLiA,3604
216
- rbx_cp-0.5.62.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
217
- rbx_cp-0.5.62.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
218
- rbx_cp-0.5.62.dist-info/RECORD,,
215
+ rbx_cp-0.5.63.dist-info/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
216
+ rbx_cp-0.5.63.dist-info/METADATA,sha256=dgNReBRlMvN09iAcedaaGLwPJ69PEfhET7Ml9yaGwpo,3604
217
+ rbx_cp-0.5.63.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
218
+ rbx_cp-0.5.63.dist-info/entry_points.txt,sha256=qBTLBOeifT1F00LWaEewRRE_jQPgvH7BUdJfZ-dYsFU,57
219
+ rbx_cp-0.5.63.dist-info/RECORD,,