1
+ # To gradually migrate to mypy we aren't setting these globally yet
2
+ # mypy: disallow_untyped_defs=True
3
+ # mypy: disallow_untyped_calls=True
4
+
5
+ import argparse
6
+ import code
1
7
import collections
2
8
import logging
3
9
import sys
16
22
from .repl import extract_exit_value
17
23
from .translations import _
18
24
25
+ from typing import (
26
+ Any ,
27
+ Dict ,
28
+ List ,
29
+ Callable ,
30
+ Union ,
31
+ Sequence ,
32
+ Tuple ,
33
+ Optional ,
34
+ Generator ,
35
+ )
36
+ from typing_extensions import Literal , Protocol
37
+
19
38
logger = logging .getLogger (__name__ )
20
39
21
40
41
+ class SupportsEventGeneration (Protocol ):
42
+ def send (
43
+ self , timeout : Union [float , None ]
44
+ ) -> Union [str , curtsies .events .Event , None ]:
45
+ ...
46
+
47
+ def __iter__ (self ) -> SupportsEventGeneration :
48
+ ...
49
+
50
+ def __next__ (self ) -> Union [str , curtsies .events .Event , None ]:
51
+ ...
52
+
53
+
22
54
class FullCurtsiesRepl (BaseRepl ):
23
- def __init__ (self , config , locals_ , banner , interp = None ) -> None :
55
+ def __init__ (
56
+ self ,
57
+ config : Config ,
58
+ locals_ : Optional [Dict [str , Any ]],
59
+ banner : Optional [str ],
60
+ interp : code .InteractiveInterpreter = None ,
61
+ ) -> None :
24
62
self .input_generator = curtsies .input .Input (
25
63
keynames = "curtsies" , sigint_event = True , paste_threshold = None
26
64
)
@@ -32,13 +70,13 @@ def __init__(self, config, locals_, banner, interp=None) -> None:
32
70
extra_bytes_callback = self .input_generator .unget_bytes ,
33
71
)
34
72
35
- self ._request_refresh_callback = self . input_generator . event_trigger (
36
- events . RefreshRequestEvent
37
- )
38
- self ._schedule_refresh_callback = (
39
- self . input_generator . scheduled_event_trigger (
40
- events . ScheduledRefreshRequestEvent
41
- )
73
+ self ._request_refresh_callback : Callable [
74
+ [], None
75
+ ] = self . input_generator . event_trigger ( events . RefreshRequestEvent )
76
+ self ._schedule_refresh_callback : Callable [
77
+ [ float ], None
78
+ ] = self . input_generator . scheduled_event_trigger (
79
+ events . ScheduledRefreshRequestEvent
42
80
)
43
81
self ._request_reload_callback = (
44
82
self .input_generator .threadsafe_event_trigger (events .ReloadEvent )
@@ -61,40 +99,42 @@ def __init__(self, config, locals_, banner, interp=None) -> None:
61
99
orig_tcattrs = self .input_generator .original_stty ,
62
100
)
63
101
64
- def _request_refresh (self ):
102
+ def _request_refresh (self ) -> None :
65
103
return self ._request_refresh_callback ()
66
104
67
- def _schedule_refresh (self , when = "now" ) :
105
+ def _schedule_refresh (self , when : float ) -> None :
68
106
return self ._schedule_refresh_callback (when )
69
107
70
- def _request_reload (self , files_modified = ("?" ,)):
108
+ def _request_reload (self , files_modified : Sequence [ str ] = ("?" ,)) -> None :
71
109
return self ._request_reload_callback (files_modified )
72
110
73
- def interrupting_refresh (self ):
111
+ def interrupting_refresh (self ) -> None :
74
112
return self ._interrupting_refresh_callback ()
75
113
76
- def request_undo (self , n = 1 ) :
114
+ def request_undo (self , n : int = 1 ) -> None :
77
115
return self ._request_undo_callback (n = n )
78
116
79
- def get_term_hw (self ):
117
+ def get_term_hw (self ) -> Tuple [ int , int ] :
80
118
return self .window .get_term_hw ()
81
119
82
- def get_cursor_vertical_diff (self ):
120
+ def get_cursor_vertical_diff (self ) -> int :
83
121
return self .window .get_cursor_vertical_diff ()
84
122
85
- def get_top_usable_line (self ):
123
+ def get_top_usable_line (self ) -> int :
86
124
return self .window .top_usable_row
87
125
88
- def on_suspend (self ):
126
+ def on_suspend (self ) -> None :
89
127
self .window .__exit__ (None , None , None )
90
128
self .input_generator .__exit__ (None , None , None )
91
129
92
- def after_suspend (self ):
130
+ def after_suspend (self ) -> None :
93
131
self .input_generator .__enter__ ()
94
132
self .window .__enter__ ()
95
133
self .interrupting_refresh ()
96
134
97
- def process_event_and_paint (self , e ):
135
+ def process_event_and_paint (
136
+ self , e : Union [str , curtsies .events .Event , None ]
137
+ ) -> None :
98
138
"""If None is passed in, just paint the screen"""
99
139
try :
100
140
if e is not None :
@@ -112,7 +152,11 @@ def process_event_and_paint(self, e):
112
152
scrolled = self .window .render_to_terminal (array , cursor_pos )
113
153
self .scroll_offset += scrolled
114
154
115
- def mainloop (self , interactive = True , paste = None ):
155
+ def mainloop (
156
+ self ,
157
+ interactive : bool = True ,
158
+ paste : Optional [curtsies .events .PasteEvent ] = None ,
159
+ ) -> None :
116
160
if interactive :
117
161
# Add custom help command
118
162
# TODO: add methods to run the code
@@ -137,14 +181,19 @@ def mainloop(self, interactive=True, paste=None):
137
181
self .process_event_and_paint (e )
138
182
139
183
140
- def main (args = None , locals_ = None , banner = None , welcome_message = None ):
184
+ def main (
185
+ args : List [str ] = None ,
186
+ locals_ : Dict [str , Any ] = None ,
187
+ banner : str = None ,
188
+ welcome_message : str = None ,
189
+ ) -> Any :
141
190
"""
142
191
banner is displayed directly after the version information.
143
192
welcome_message is passed on to Repl and displayed in the statusbar.
144
193
"""
145
194
translations .init ()
146
195
147
- def curtsies_arguments (parser ) :
196
+ def curtsies_arguments (parser : argparse . _ArgumentGroup ) -> None :
148
197
parser .add_argument (
149
198
"--paste" ,
150
199
"-p" ,
@@ -163,10 +212,10 @@ def curtsies_arguments(parser):
163
212
164
213
interp = None
165
214
paste = None
215
+ exit_value : Tuple [Any , ...] = ()
166
216
if exec_args :
167
217
if not options :
168
218
raise ValueError ("don't pass in exec_args without options" )
169
- exit_value = ()
170
219
if options .paste :
171
220
paste = curtsies .events .PasteEvent ()
172
221
encoding = inspection .get_encoding_file (exec_args [0 ])
@@ -196,16 +245,20 @@ def curtsies_arguments(parser):
196
245
with repl .window as win :
197
246
with repl :
198
247
repl .height , repl .width = win .t .height , win .t .width
199
- exit_value = repl .mainloop (True , paste )
248
+ repl .mainloop (True , paste )
200
249
except (SystemExitFromCodeRunner , SystemExit ) as e :
201
250
exit_value = e .args
202
251
return extract_exit_value (exit_value )
203
252
204
253
205
- def _combined_events (event_provider , paste_threshold ):
254
+ def _combined_events (
255
+ event_provider : SupportsEventGeneration , paste_threshold : int
256
+ ) -> Generator [
257
+ Union [str , curtsies .events .Event , None ], Union [float , None ], None
258
+ ]:
206
259
"""Combines consecutive keypress events into paste events."""
207
260
timeout = yield "nonsense_event" # so send can be used immediately
208
- queue = collections .deque ()
261
+ queue : collections . deque = collections .deque ()
209
262
while True :
210
263
e = event_provider .send (timeout )
211
264
if isinstance (e , curtsies .events .Event ):
@@ -230,7 +283,9 @@ def _combined_events(event_provider, paste_threshold):
230
283
timeout = yield queue .popleft ()
231
284
232
285
233
- def combined_events (event_provider , paste_threshold = 3 ):
286
+ def combined_events (
287
+ event_provider : SupportsEventGeneration , paste_threshold : int = 3
288
+ ) -> SupportsEventGeneration :
234
289
g = _combined_events (event_provider , paste_threshold )
235
290
next (g )
236
291
return g
0 commit comments