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
17import collections
28import logging
39import sys
1622from .repl import extract_exit_value
1723from .translations import _
1824
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+
1938logger = logging .getLogger (__name__ )
2039
2140
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+
2254class 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 :
2462 self .input_generator = curtsies .input .Input (
2563 keynames = "curtsies" , sigint_event = True , paste_threshold = None
2664 )
@@ -32,13 +70,13 @@ def __init__(self, config, locals_, banner, interp=None) -> None:
3270 extra_bytes_callback = self .input_generator .unget_bytes ,
3371 )
3472
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
4280 )
4381 self ._request_reload_callback = (
4482 self .input_generator .threadsafe_event_trigger (events .ReloadEvent )
@@ -61,40 +99,42 @@ def __init__(self, config, locals_, banner, interp=None) -> None:
6199 orig_tcattrs = self .input_generator .original_stty ,
62100 )
63101
64- def _request_refresh (self ):
102+ def _request_refresh (self ) -> None :
65103 return self ._request_refresh_callback ()
66104
67- def _schedule_refresh (self , when = "now" ) :
105+ def _schedule_refresh (self , when : float ) -> None :
68106 return self ._schedule_refresh_callback (when )
69107
70- def _request_reload (self , files_modified = ("?" ,)):
108+ def _request_reload (self , files_modified : Sequence [ str ] = ("?" ,)) -> None :
71109 return self ._request_reload_callback (files_modified )
72110
73- def interrupting_refresh (self ):
111+ def interrupting_refresh (self ) -> None :
74112 return self ._interrupting_refresh_callback ()
75113
76- def request_undo (self , n = 1 ) :
114+ def request_undo (self , n : int = 1 ) -> None :
77115 return self ._request_undo_callback (n = n )
78116
79- def get_term_hw (self ):
117+ def get_term_hw (self ) -> Tuple [ int , int ] :
80118 return self .window .get_term_hw ()
81119
82- def get_cursor_vertical_diff (self ):
120+ def get_cursor_vertical_diff (self ) -> int :
83121 return self .window .get_cursor_vertical_diff ()
84122
85- def get_top_usable_line (self ):
123+ def get_top_usable_line (self ) -> int :
86124 return self .window .top_usable_row
87125
88- def on_suspend (self ):
126+ def on_suspend (self ) -> None :
89127 self .window .__exit__ (None , None , None )
90128 self .input_generator .__exit__ (None , None , None )
91129
92- def after_suspend (self ):
130+ def after_suspend (self ) -> None :
93131 self .input_generator .__enter__ ()
94132 self .window .__enter__ ()
95133 self .interrupting_refresh ()
96134
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 :
98138 """If None is passed in, just paint the screen"""
99139 try :
100140 if e is not None :
@@ -112,7 +152,11 @@ def process_event_and_paint(self, e):
112152 scrolled = self .window .render_to_terminal (array , cursor_pos )
113153 self .scroll_offset += scrolled
114154
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 :
116160 if interactive :
117161 # Add custom help command
118162 # TODO: add methods to run the code
@@ -137,14 +181,19 @@ def mainloop(self, interactive=True, paste=None):
137181 self .process_event_and_paint (e )
138182
139183
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 :
141190 """
142191 banner is displayed directly after the version information.
143192 welcome_message is passed on to Repl and displayed in the statusbar.
144193 """
145194 translations .init ()
146195
147- def curtsies_arguments (parser ) :
196+ def curtsies_arguments (parser : argparse . _ArgumentGroup ) -> None :
148197 parser .add_argument (
149198 "--paste" ,
150199 "-p" ,
@@ -163,10 +212,10 @@ def curtsies_arguments(parser):
163212
164213 interp = None
165214 paste = None
215+ exit_value : Tuple [Any , ...] = ()
166216 if exec_args :
167217 if not options :
168218 raise ValueError ("don't pass in exec_args without options" )
169- exit_value = ()
170219 if options .paste :
171220 paste = curtsies .events .PasteEvent ()
172221 encoding = inspection .get_encoding_file (exec_args [0 ])
@@ -196,16 +245,20 @@ def curtsies_arguments(parser):
196245 with repl .window as win :
197246 with repl :
198247 repl .height , repl .width = win .t .height , win .t .width
199- exit_value = repl .mainloop (True , paste )
248+ repl .mainloop (True , paste )
200249 except (SystemExitFromCodeRunner , SystemExit ) as e :
201250 exit_value = e .args
202251 return extract_exit_value (exit_value )
203252
204253
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+ ]:
206259 """Combines consecutive keypress events into paste events."""
207260 timeout = yield "nonsense_event" # so send can be used immediately
208- queue = collections .deque ()
261+ queue : collections . deque = collections .deque ()
209262 while True :
210263 e = event_provider .send (timeout )
211264 if isinstance (e , curtsies .events .Event ):
@@ -230,7 +283,9 @@ def _combined_events(event_provider, paste_threshold):
230283 timeout = yield queue .popleft ()
231284
232285
233- def combined_events (event_provider , paste_threshold = 3 ):
286+ def combined_events (
287+ event_provider : SupportsEventGeneration , paste_threshold : int = 3
288+ ) -> SupportsEventGeneration :
234289 g = _combined_events (event_provider , paste_threshold )
235290 next (g )
236291 return g
0 commit comments