repl-toolkit 1.2.0__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.
- repl_toolkit/__init__.py +70 -0
- repl_toolkit/actions/__init__.py +24 -0
- repl_toolkit/actions/action.py +223 -0
- repl_toolkit/actions/registry.py +564 -0
- repl_toolkit/async_repl.py +374 -0
- repl_toolkit/completion/__init__.py +15 -0
- repl_toolkit/completion/prefix.py +109 -0
- repl_toolkit/completion/shell_expansion.py +453 -0
- repl_toolkit/formatting.py +152 -0
- repl_toolkit/headless_repl.py +251 -0
- repl_toolkit/ptypes.py +122 -0
- repl_toolkit/tests/__init__.py +5 -0
- repl_toolkit/tests/conftest.py +79 -0
- repl_toolkit/tests/test_actions.py +578 -0
- repl_toolkit/tests/test_async_repl.py +381 -0
- repl_toolkit/tests/test_completion.py +656 -0
- repl_toolkit/tests/test_formatting.py +232 -0
- repl_toolkit/tests/test_headless.py +677 -0
- repl_toolkit/tests/test_types.py +174 -0
- repl_toolkit-1.2.0.dist-info/METADATA +761 -0
- repl_toolkit-1.2.0.dist-info/RECORD +24 -0
- repl_toolkit-1.2.0.dist-info/WHEEL +5 -0
- repl_toolkit-1.2.0.dist-info/licenses/LICENSE +21 -0
- repl_toolkit-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for protocol compliance and type checking.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
from unittest.mock import AsyncMock, Mock
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from repl_toolkit.actions import ActionRegistry
|
|
11
|
+
from repl_toolkit.ptypes import ActionHandler, AsyncBackend, Completer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestProtocolCompliance:
|
|
15
|
+
"""Test protocol compliance for various interfaces."""
|
|
16
|
+
|
|
17
|
+
def test_async_backend_protocol(self):
|
|
18
|
+
"""Test AsyncBackend protocol compliance."""
|
|
19
|
+
|
|
20
|
+
class TestAsyncBackend:
|
|
21
|
+
async def handle_input(self, user_input: str) -> bool:
|
|
22
|
+
return True
|
|
23
|
+
|
|
24
|
+
backend = TestAsyncBackend()
|
|
25
|
+
assert isinstance(backend, AsyncBackend)
|
|
26
|
+
assert hasattr(backend, "handle_input")
|
|
27
|
+
|
|
28
|
+
def test_action_handler_protocol(self):
|
|
29
|
+
"""Test ActionHandler protocol compliance."""
|
|
30
|
+
|
|
31
|
+
class TestActionHandler:
|
|
32
|
+
def execute_action(self, action_name: str, context) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def handle_command(self, command: str) -> None:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
def validate_action(self, action_name: str) -> bool:
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
def list_actions(self) -> list:
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
handler = TestActionHandler()
|
|
45
|
+
assert isinstance(handler, ActionHandler)
|
|
46
|
+
assert hasattr(handler, "execute_action")
|
|
47
|
+
assert hasattr(handler, "handle_command")
|
|
48
|
+
assert hasattr(handler, "validate_action")
|
|
49
|
+
assert hasattr(handler, "list_actions")
|
|
50
|
+
|
|
51
|
+
def test_completer_protocol(self):
|
|
52
|
+
"""Test Completer protocol compliance."""
|
|
53
|
+
|
|
54
|
+
class TestCompleter:
|
|
55
|
+
def get_completions(self, document, complete_event):
|
|
56
|
+
return []
|
|
57
|
+
|
|
58
|
+
completer = TestCompleter()
|
|
59
|
+
assert isinstance(completer, Completer)
|
|
60
|
+
assert hasattr(completer, "get_completions")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TestMockBackendCompliance:
|
|
64
|
+
"""Test mock backend implementations for protocol compliance."""
|
|
65
|
+
|
|
66
|
+
def test_mock_backend_async_protocol(self):
|
|
67
|
+
"""Test mock backend implements AsyncBackend protocol."""
|
|
68
|
+
mock_backend = Mock(spec=AsyncBackend)
|
|
69
|
+
mock_backend.handle_input = AsyncMock(return_value=True)
|
|
70
|
+
|
|
71
|
+
# Should have required method
|
|
72
|
+
assert hasattr(mock_backend, "handle_input")
|
|
73
|
+
assert callable(mock_backend.handle_input)
|
|
74
|
+
|
|
75
|
+
@pytest.mark.asyncio
|
|
76
|
+
async def test_mock_backend_functionality(self):
|
|
77
|
+
"""Test mock backend functionality."""
|
|
78
|
+
mock_backend = Mock(spec=AsyncBackend)
|
|
79
|
+
mock_backend.handle_input = AsyncMock(return_value=True)
|
|
80
|
+
|
|
81
|
+
result = await mock_backend.handle_input("test input")
|
|
82
|
+
assert result is True
|
|
83
|
+
mock_backend.handle_input.assert_called_once_with("test input")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TestRegistryProtocolCompliance:
|
|
87
|
+
"""Test ActionRegistry protocol compliance."""
|
|
88
|
+
|
|
89
|
+
def setup_method(self):
|
|
90
|
+
"""Set up test registry."""
|
|
91
|
+
self.registry = ActionRegistry()
|
|
92
|
+
|
|
93
|
+
def test_implements_action_handler(self):
|
|
94
|
+
"""Test registry implements ActionHandler protocol."""
|
|
95
|
+
assert isinstance(self.registry, ActionHandler)
|
|
96
|
+
|
|
97
|
+
def test_execute_action_method(self):
|
|
98
|
+
"""Test execute_action method signature."""
|
|
99
|
+
from repl_toolkit.actions import ActionContext
|
|
100
|
+
|
|
101
|
+
context = ActionContext(registry=self.registry)
|
|
102
|
+
|
|
103
|
+
# Should not raise error for built-in action
|
|
104
|
+
self.registry.execute_action("show_help", context)
|
|
105
|
+
|
|
106
|
+
def test_handle_command_method(self):
|
|
107
|
+
"""Test handle_command method signature."""
|
|
108
|
+
# Should not raise error for unknown command (now synchronous)
|
|
109
|
+
self.registry.handle_command("/unknown")
|
|
110
|
+
|
|
111
|
+
def test_validate_action_method(self):
|
|
112
|
+
"""Test validate_action method signature."""
|
|
113
|
+
assert self.registry.validate_action("show_help") is True
|
|
114
|
+
assert self.registry.validate_action("nonexistent") is False
|
|
115
|
+
|
|
116
|
+
def test_list_actions_method(self):
|
|
117
|
+
"""Test list_actions method signature."""
|
|
118
|
+
actions = self.registry.list_actions()
|
|
119
|
+
assert isinstance(actions, list)
|
|
120
|
+
assert len(actions) > 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TestBackendUsagePatterns:
|
|
124
|
+
"""Test common backend usage patterns for both interactive and headless modes."""
|
|
125
|
+
|
|
126
|
+
def test_backend_for_interactive_mode(self):
|
|
127
|
+
"""Test backend suitable for interactive mode."""
|
|
128
|
+
|
|
129
|
+
class InteractiveBackend:
|
|
130
|
+
async def handle_input(self, user_input: str) -> bool:
|
|
131
|
+
# Interactive backends might do complex processing
|
|
132
|
+
print(f"Processing: {user_input}")
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
backend = InteractiveBackend()
|
|
136
|
+
assert isinstance(backend, AsyncBackend)
|
|
137
|
+
|
|
138
|
+
def test_backend_for_headless_mode(self):
|
|
139
|
+
"""Test backend suitable for headless mode."""
|
|
140
|
+
|
|
141
|
+
class HeadlessBackend:
|
|
142
|
+
def __init__(self):
|
|
143
|
+
self.results = []
|
|
144
|
+
|
|
145
|
+
async def handle_input(self, user_input: str) -> bool:
|
|
146
|
+
# Headless backends might accumulate results
|
|
147
|
+
self.results.append(f"Processed: {user_input}")
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
backend = HeadlessBackend()
|
|
151
|
+
assert isinstance(backend, AsyncBackend)
|
|
152
|
+
assert hasattr(backend, "results")
|
|
153
|
+
|
|
154
|
+
def test_unified_backend_for_both_modes(self):
|
|
155
|
+
"""Test backend that works for both interactive and headless modes."""
|
|
156
|
+
|
|
157
|
+
class UnifiedBackend:
|
|
158
|
+
def __init__(self, mode="interactive"):
|
|
159
|
+
self.mode = mode
|
|
160
|
+
self.results = []
|
|
161
|
+
|
|
162
|
+
async def handle_input(self, user_input: str) -> bool:
|
|
163
|
+
if self.mode == "interactive":
|
|
164
|
+
print(f"Interactive: {user_input}")
|
|
165
|
+
else:
|
|
166
|
+
self.results.append(user_input)
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
# Same backend class works for both modes
|
|
170
|
+
interactive_backend = UnifiedBackend("interactive")
|
|
171
|
+
headless_backend = UnifiedBackend("headless")
|
|
172
|
+
|
|
173
|
+
assert isinstance(interactive_backend, AsyncBackend)
|
|
174
|
+
assert isinstance(headless_backend, AsyncBackend)
|