textual-reflect 0.1.1__py3-none-any.whl → 0.1.6__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.
@@ -2,4 +2,4 @@
2
2
 
3
3
  from .reflect import Reflector
4
4
 
5
- __version__ = "0.1.1"
5
+ __version__ = "0.1.6"
@@ -1,6 +1,36 @@
1
1
  """Entry point for running the Textual ReflectorApp as a module."""
2
2
 
3
- from .reflect import ReflectorApp
3
+ if __name__ == "__main__":
4
+ from reflect import Reflector
5
+ else:
6
+ from .reflect import Reflector
7
+
8
+ from textual.app import App, ComposeResult
9
+ from textual.widgets import Footer, Header
10
+
11
+ class ReflectorApp(App):
12
+ BINDINGS = [("ctrl+t", "toggle_theme", "toggle theme")]
13
+
14
+ def compose(self) -> ComposeResult:
15
+ self.header = Header(id="header", icon="🐍")
16
+ self.reflector = Reflector(id="reflector")
17
+ self.footer = Footer(id="footer")
18
+
19
+ yield self.header
20
+ yield self.reflector
21
+ yield self.footer
22
+
23
+ def on_mount(self) -> None:
24
+ self.title = "Reflector"
25
+ self.sub_title = "Demo"
26
+ self.theme = "monokai"
27
+ self.reflector.input.theme = "monokai"
28
+
29
+ def action_toggle_theme(self) -> None:
30
+ themes = ["dracula", "monokai"]
31
+ theme = "dracula" if self.theme == "monokai" else "monokai"
32
+ self.theme = theme
33
+ self.reflector.input.theme = theme
4
34
 
5
35
  if __name__ == "__main__":
6
36
  app = ReflectorApp()
@@ -1,43 +1,123 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+
4
+ from __future__ import annotations
5
+
3
6
  import sys
4
- from code import InteractiveConsole
7
+ from code import interact, InteractiveConsole
5
8
  from io import StringIO
6
- from random import choice
9
+ from typing import TYPE_CHECKING
7
10
 
8
11
  from rich.syntax import Syntax
9
- from textual.app import App, ComposeResult
12
+ from textual.app import ComposeResult
10
13
  from textual.containers import Container, Horizontal
11
14
  from textual.widget import Widget
12
- from textual.widgets import Footer, Header, RichLog, TextArea
15
+ from textual.widgets import RichLog, TextArea
16
+
17
+ if TYPE_CHECKING:
18
+ from typing_extensions import Self
13
19
 
14
20
 
15
21
  class Reflector(Widget):
22
+ # def __init__(self):
23
+ # pass
24
+
16
25
  BINDINGS = [
17
26
  ("ctrl+r", "eval", "eval"),
18
27
  ("ctrl+n", "dir", "namespace"),
19
28
  ("ctrl+l", "clear_output", "clear output"),
20
29
  ("ctrl+s", "clear_input", "clear input"),
21
30
  ]
31
+
32
+ DEFAULT_CSS = """
33
+ #reflector-input {
34
+ padding: 0 1 0 1;
35
+ border: none;
36
+ background: $surface;
37
+ }
38
+
39
+ #reflector-output {
40
+ padding: 0 1 0 1;
41
+ background: $surface;
42
+ }
43
+
44
+ #reflector-input-container {
45
+ border: solid $primary;
46
+ height: 0.4fr;
47
+ margin: 0 1 0 1;
48
+ background: $background;
49
+ }
50
+
51
+ #reflector-input-container:focus-within {
52
+ border: solid $accent;
53
+ }
54
+
55
+ #reflector-output-container {
56
+ border: solid $primary;
57
+ margin: 0 1 0 1;
58
+ height: 0.6fr;
59
+ background: $background;
60
+ }
61
+
62
+ #reflector-container {
63
+ border: solid $primary;
64
+ background: $background;
65
+ margin: 0 1 0 1;
66
+ }
67
+ """
68
+
22
69
 
23
- def compose(self) -> ComposeResult:
70
+ def compose(self) -> ComposeResult:
24
71
  self.input = TextArea.code_editor(
25
72
  id="reflector-input",
26
73
  language="python",
27
- placeholder="Press ^r to evaluate...",
74
+ show_line_numbers=False,
75
+ soft_wrap=True,
76
+ placeholder="Press ^r to evaluate.",
77
+ )
78
+
79
+ self.input_container = Container(
80
+ self.input,
81
+ id="reflector-input-container"
82
+ )
83
+
84
+ self.output = RichLog(
85
+ id="reflector-output",
86
+ markup=True,
87
+ highlight=True,
88
+ # min_width=80,
89
+ # wrap=True
90
+ )
91
+
92
+ self.output_container = Container(
93
+ self.output,
94
+ id="reflector-output-container"
95
+ )
96
+
97
+ self.container = Container(
98
+ self.output_container,
99
+ self.input_container,
100
+ id="reflector-container"
28
101
  )
29
- self.input_container = Container(self.input, id="reflector-input-container")
30
- self.output = RichLog(id="reflector-output", markup=True, highlight=True)
31
- self.output_container = Container(self.output, id="reflector-output-container")
32
102
 
33
- yield self.output_container
34
- yield self.input_container
103
+ yield self.container
104
+
35
105
 
36
106
  def on_mount(self) -> None:
37
- self.input_container.border_title = "Input"
38
- self.output_container.border_title = "Output"
107
+ self.stdout, self.stderr = sys.stdout, sys.stderr
108
+ self.more_input = False
109
+ self.prompt = ">>> "
110
+ self.input_container.border_title = f"{self.prompt}"
111
+ self.output_container.border_title = f"{self.app.title}"
112
+ self.input_container.border_subtitle = "Input"
113
+ self.output_container.border_subtitle = "Output"
39
114
  self.namespace = {"app": self.app, "__builtins__": __builtins__}
40
115
  self.repl = InteractiveConsole(locals=self.namespace)
116
+ self.banner = f"""\
117
+ Python {sys.version} on {sys.platform}
118
+ Type "help", "copyright", "credits" or "license" for more information.
119
+ """
120
+ self.write(self.banner)
41
121
  self.input.focus()
42
122
 
43
123
  def action_dir(self) -> None:
@@ -49,86 +129,44 @@ class Reflector(Widget):
49
129
  def action_clear_input(self) -> None:
50
130
  self.input.clear()
51
131
 
52
- def action_eval(self, code="") -> None:
53
- if not code:
54
- code = self.input.text
55
- if not code:
56
- return
132
+ def write(self, content:str="") -> Self:
133
+ return self.output.write(Syntax(content, "python", indent_guides=True))
57
134
 
58
- split_code = code.split("\n")
59
- self.output.write(Syntax(f">>> {split_code[0]}", "python", indent_guides=True))
60
-
61
- if len(split_code) > 1:
62
- for line in split_code[1:]:
63
- self.output.write(Syntax(f"... {line}", "python", indent_guides=True))
64
- self.output.write(Syntax("... ", "python", indent_guides=True))
65
-
66
- old_stdout, old_stderr = sys.stdout, sys.stderr
135
+ def redirect_io(self):
67
136
  sys.stdout, sys.stderr = StringIO(), StringIO()
68
- self.repl.push(code + "\n")
69
- captured_output = sys.stdout.getvalue().strip()
70
- captured_error = sys.stderr.getvalue().strip()
71
137
 
72
- if captured_output:
73
- self.output.write(Syntax(captured_output, "python", indent_guides=True))
74
- if captured_error:
75
- self.output.write(Syntax(captured_error, "python", indent_guides=True))
76
-
77
- sys.stdout, sys.stderr = old_stdout, old_stderr
78
- self.input.clear()
79
- self.input.focus()
138
+ def restore_io(self):
139
+ sys.stdout, sys.stderr = self.stdout, self.stderr
140
+
141
+ def action_eval(self, code="", capture=False) -> Tuple[str, str]|None:
142
+ if not code:
143
+ code = self.input.text
80
144
 
145
+ for line in code.split("\n"):
146
+ self.write(f"{self.prompt}{line}")
147
+ self.redirect_io()
148
+ self.more_input = self.repl.push(line)
149
+ captured_output = sys.stdout.getvalue().strip()
150
+ captured_error = sys.stderr.getvalue().strip()
151
+ self.restore_io()
81
152
 
82
- class ReflectorApp(App):
83
- CSS = """
84
- #reflector-input {
85
- padding: 1 2 1 2;
86
- border: none;
87
- background: $surface;
88
- }
89
-
90
- #reflector-output {
91
- padding: 1 2 1 2;
92
- background: $panel;
93
- }
94
-
95
- #reflector-input-container {
96
- border: solid $primary;
97
- height: 0.4fr;
98
- margin: 0 2 1 2;
99
- background: $background;
100
- }
153
+ if captured_output:
154
+ self.write(captured_output)
101
155
 
102
- #reflector-input-container:focus-within {
103
- border: solid $accent;
104
- }
105
-
106
- #reflector-output-container {
107
- border: solid $primary;
108
- margin: 1 2 1 2;
109
- height: 0.6fr;
110
- background: $background;
111
- }
112
- """
156
+ if captured_error:
157
+ self.write(captured_error)
113
158
 
114
- BINDINGS = [("ctrl+t", "toggle_theme", "toggle theme")]
159
+ if self.more_input:
160
+ self.prompt = "... "
161
+ # self.input_container.styles.border = ("solid", "yellow")
162
+ else:
163
+ self.prompt = ">>> "
164
+ # self.input_container.styles.border = self.accent
115
165
 
116
- def compose(self) -> ComposeResult:
117
- self.header = Header(id="header", icon="🐍")
118
- self.reflector = Reflector(id="reflector")
119
- self.footer = Footer(id="footer")
166
+ self.input_container.border_title = f"{self.prompt}"
120
167
 
121
- yield self.header
122
- yield self.reflector
123
- yield self.footer
168
+ self.input.clear()
169
+ self.input.focus()
124
170
 
125
- def on_mount(self) -> None:
126
- self.sub_title = "ReflectorWidget"
127
- self.theme = "monokai"
128
- self.reflector.input.theme = "monokai"
129
-
130
- def action_toggle_theme(self) -> None:
131
- themes = ["dracula", "monokai"]
132
- theme = "dracula" if self.theme == "monokai" else "monokai"
133
- self.theme = theme
134
- self.reflector.input.theme = theme
171
+ if capture:
172
+ return captured_output, captured_error
@@ -0,0 +1,28 @@
1
+ #reflector-input {
2
+ padding: 1 2 1 2;
3
+ border: none;
4
+ background: $surface;
5
+ }
6
+
7
+ #reflector-output {
8
+ padding: 1 2 1 2;
9
+ background: $panel;
10
+ }
11
+
12
+ #reflector-input-container {
13
+ border: solid $primary;
14
+ height: 0.4fr;
15
+ margin: 0 2 1 2;
16
+ background: $background;
17
+ }
18
+
19
+ #reflector-input-container:focus-within {
20
+ border: solid $accent;
21
+ }
22
+
23
+ #reflector-output-container {
24
+ border: solid $primary;
25
+ margin: 1 2 1 2;
26
+ height: 0.6fr;
27
+ background: $background;
28
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: textual-reflect
3
- Version: 0.1.1
3
+ Version: 0.1.6
4
4
  Summary: A Textual widget for code reflection and introspection.
5
5
  Project-URL: Homepage, https://github.com/Ismael-VC/textual-reflect
6
6
  Project-URL: Repository, https://github.com/Ismael-VC/textual-reflect.git
@@ -44,3 +44,14 @@ if __name__ == "__main__":
44
44
  app = MyApp()
45
45
  app.run()
46
46
  ```
47
+
48
+ TODO
49
+
50
+ - input accept empty string, write empty line, push \r
51
+ - use sys prompts and return value
52
+ - capture banner
53
+ - 1 big container
54
+ - force vertical
55
+ - vertical group?
56
+ - animation top down
57
+ - log not focusable
@@ -0,0 +1,8 @@
1
+ textual_reflect/__init__.py,sha256=ex3-Ib9mxRdvC8B8olg5S7ly3nK1dBf675WCOL7POrg,87
2
+ textual_reflect/__main__.py,sha256=3AHjPr0aTEtp4vGfCkKWyvP5QPz1LZ9wrZxw8p80wUU,1080
3
+ textual_reflect/reflect.py,sha256=iPJcMbZCSLkWrC30vqx-QD2yh1AqjI3ZQ2QkZh6-SrY,4819
4
+ textual_reflect/reflect.tcss,sha256=5iidcsmgFDSnblOTh29wWVTwuFgMCC0oSQuBcRiK3OA,487
5
+ textual_reflect-0.1.6.dist-info/METADATA,sha256=HhVh4UIt5JCq5pDvwQ4xy-eISg9mbyuRf8IBw459mSM,1475
6
+ textual_reflect-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
+ textual_reflect-0.1.6.dist-info/licenses/LICENSE,sha256=-f4_jKU5jZQN4f13AVWYr07_FjbWLmSXVKLe72_jaGk,1089
8
+ textual_reflect-0.1.6.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- textual_reflect/__init__.py,sha256=0JUnTC0BZ2fzNDpyQ5nwD65r0iETbU_olIu-vXrPLwg,87
2
- textual_reflect/__main__.py,sha256=1bCP4V0glYB-Fn7mBq1Pp12pkDQVoaFpE1dYvWuLvO4,170
3
- textual_reflect/reflect.py,sha256=FHk2iFM3hxWrq3oTjvix3bLC2UqONnUaocrkMoutyNc,4085
4
- textual_reflect-0.1.1.dist-info/METADATA,sha256=dJ726z6fu9xY1mVB4GWM4AuML5Y7M-fGHX6r4SNC32Q,1267
5
- textual_reflect-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
6
- textual_reflect-0.1.1.dist-info/licenses/LICENSE,sha256=-f4_jKU5jZQN4f13AVWYr07_FjbWLmSXVKLe72_jaGk,1089
7
- textual_reflect-0.1.1.dist-info/RECORD,,