1+ from __future__ import annotations
12from builtins import range
23import traceback
34from io import open
5+ from typing import Any , Dict , List , Set , Tuple , Optional , Callable , TYPE_CHECKING
6+
7+ if TYPE_CHECKING :
8+ from geventwebsocket .websocket import WebSocket
49
510from gevent .threading import Timer
611import gevent as gvt
1823import mimetypes
1924
2025mimetypes .add_type ('application/javascript' , '.js' )
21- _eel_js_file = pkg .resource_filename ('eel' , 'eel.js' )
22- _eel_js = open (_eel_js_file , encoding = 'utf-8' ).read ()
23- _websockets = []
24- _call_return_values = {}
25- _call_return_callbacks = {}
26- _call_number = 0
27- _exposed_functions = {}
28- _js_functions = []
29- _mock_queue = []
30- _mock_queue_done = set ()
31- _shutdown = None
26+ _eel_js_file : str = pkg .resource_filename ('eel' , 'eel.js' )
27+ _eel_js : str = open (_eel_js_file , encoding = 'utf-8' ).read ()
28+ _websockets : List [Tuple [Any , WebSocket ]] = []
29+ _call_return_values : Dict [Any , Any ] = {}
30+ _call_return_callbacks : Dict [float , Tuple [Callable [..., Any ], Optional [Callable [..., Any ]]]] = {}
31+ _call_number : int = 0
32+ _exposed_functions : Dict [Any , Any ] = {}
33+ _js_functions : List [Any ] = []
34+ _mock_queue : List [Any ] = []
35+ _mock_queue_done : Set [Any ] = set ()
36+ _shutdown : Optional [gvt .Greenlet ] = None # Later assigned as global by _websocket_close()
37+ root_path : str # Later assigned as global by init()
3238
3339# The maximum time (in milliseconds) that Python will try to retrieve a return value for functions executing in JS
3440# Can be overridden through `eel.init` with the kwarg `js_result_timeout` (default: 10000)
35- _js_result_timeout = 10000
41+ _js_result_timeout : int = 10000
3642
3743# All start() options must provide a default value and explanation here
38- _start_args = {
44+ _start_args : dict [ str , Any ] = {
3945 'mode' : 'chrome' , # What browser is used
4046 'host' : 'localhost' , # Hostname use for Bottle server
4147 'port' : 8000 , # Port used for Bottle server (use 0 for auto)
5157 'disable_cache' : True , # Sets the no-store response header when serving assets
5258 'default_path' : 'index.html' , # The default file to retrieve for the root URL
5359 'app' : btl .default_app (), # Allows passing in a custom Bottle instance, e.g. with middleware
54- 'shutdown_delay' : 1.0 # how long to wait after a websocket closes before detecting complete shutdown
60+ 'shutdown_delay' : 1.0 # how long to wait after a websocket closes before detecting complete shutdown
5561}
5662
5763# == Temporary (suppressible) error message to inform users of breaking API change for v1.0.0 ===
5864_start_args ['suppress_error' ] = False
59- api_error_message = '''
65+ api_error_message : str = '''
6066----------------------------------------------------------------------------------
6167 'options' argument deprecated in v1.0.0, see https://github.com/ChrisKnott/Eel
6268 To suppress this error, add 'suppress_error=True' to start() call.
6773
6874# Public functions
6975
70- def expose (name_or_function = None ):
76+ def expose (name_or_function : Optional [ Callable [..., Any ]] = None ) -> Callable [..., Any ] :
7177 # Deal with '@eel.expose()' - treat as '@eel.expose'
7278 if name_or_function is None :
7379 return expose
7480
75- if type (name_or_function ) == str : # Called as '@eel.expose("my_name")'
81+ if isinstance (name_or_function , str ) : # Called as '@eel.expose("my_name")'
7682 name = name_or_function
7783
78- def decorator (function ) :
84+ def decorator (function : Callable [..., Any ]) -> Any :
7985 _expose (name , function )
8086 return function
8187 return decorator
@@ -87,7 +93,7 @@ def decorator(function):
8793
8894# PyParsing grammar for parsing exposed functions in JavaScript code
8995# Examples: `eel.expose(w, "func_name")`, `eel.expose(func_name)`, `eel.expose((function (e){}), "func_name")`
90- EXPOSED_JS_FUNCTIONS = pp .ZeroOrMore (
96+ EXPOSED_JS_FUNCTIONS : pp . ZeroOrMore = pp .ZeroOrMore (
9197 pp .Suppress (
9298 pp .SkipTo (pp .Literal ('eel.expose(' ))
9399 + pp .Literal ('eel.expose(' )
@@ -101,8 +107,8 @@ def decorator(function):
101107)
102108
103109
104- def init (path , allowed_extensions = ['.js' , '.html' , '.txt' , '.htm' ,
105- '.xhtml' , '.vue' ], js_result_timeout = 10000 ):
110+ def init (path : str , allowed_extensions : List [ str ] = ['.js' , '.html' , '.txt' , '.htm' ,
111+ '.xhtml' , '.vue' ], js_result_timeout : int = 10000 ) -> None :
106112 global root_path , _js_functions , _js_result_timeout
107113 root_path = _get_real_path (path )
108114
@@ -133,7 +139,7 @@ def init(path, allowed_extensions=['.js', '.html', '.txt', '.htm',
133139 _js_result_timeout = js_result_timeout
134140
135141
136- def start (* start_urls , ** kwargs ) :
142+ def start (* start_urls : str , ** kwargs : Any ) -> None :
137143 _start_args .update (kwargs )
138144
139145 if 'options' in kwargs :
@@ -162,25 +168,25 @@ def start(*start_urls, **kwargs):
162168 # Launch the browser to the starting URLs
163169 show (* start_urls )
164170
165- def run_lambda ():
171+ def run_lambda () -> None :
166172 if _start_args ['all_interfaces' ] == True :
167173 HOST = '0.0.0.0'
168174 else :
169175 HOST = _start_args ['host' ]
170176
171- app = _start_args ['app' ] # type: btl.Bottle
177+ app = _start_args ['app' ]
172178
173179 if isinstance (app , btl .Bottle ):
174180 register_eel_routes (app )
175181 else :
176182 register_eel_routes (btl .default_app ())
177183
178- return btl .run (
184+ btl .run (
179185 host = HOST ,
180186 port = _start_args ['port' ],
181187 server = wbs .GeventWebSocketServer ,
182188 quiet = True ,
183- app = app )
189+ app = app ) # Always returns None
184190
185191 # Start the webserver
186192 if _start_args ['block' ]:
@@ -189,20 +195,20 @@ def run_lambda():
189195 spawn (run_lambda )
190196
191197
192- def show (* start_urls ) :
193- brw .open (start_urls , _start_args )
198+ def show (* start_urls : str ) -> None :
199+ brw .open (list ( start_urls ) , _start_args )
194200
195201
196- def sleep (seconds ) :
197- gvt .sleep (seconds )
202+ def sleep (seconds : int | float ) -> None :
203+ gvt .sleep (seconds ) # type: ignore # gevent docs specify int | float, available stubs are wrong
198204
199205
200- def spawn (function , * args , ** kwargs ) :
206+ def spawn (function : Callable [..., Any ], * args : Any , ** kwargs : Any ) -> gvt . Greenlet :
201207 return gvt .spawn (function , * args , ** kwargs )
202208
203209# Bottle Routes
204210
205- def _eel ():
211+ def _eel () -> str :
206212 start_geometry = {'default' : {'size' : _start_args ['size' ],
207213 'position' : _start_args ['position' ]},
208214 'pages' : _start_args ['geometry' ]}
@@ -215,10 +221,10 @@ def _eel():
215221 _set_response_headers (btl .response )
216222 return page
217223
218- def _root ():
224+ def _root () -> Optional [ btl . Response ] :
219225 return _static (_start_args ['default_path' ])
220226
221- def _static (path ) :
227+ def _static (path : str ) -> Optional [ btl . HTTPResponse | btl . HTTPError ] :
222228 response = None
223229 if 'jinja_env' in _start_args and 'jinja_templates' in _start_args :
224230 template_prefix = _start_args ['jinja_templates' ] + '/'
@@ -233,7 +239,7 @@ def _static(path):
233239 _set_response_headers (response )
234240 return response
235241
236- def _websocket (ws ) :
242+ def _websocket (ws : WebSocket ) -> None :
237243 global _websockets
238244
239245 for js_function in _js_functions :
@@ -259,14 +265,14 @@ def _websocket(ws):
259265 _websocket_close (page )
260266
261267
262- BOTTLE_ROUTES = {
268+ BOTTLE_ROUTES : Dict [ str , Tuple [ Callable [..., Any ], dict [ Any , Any ]]] = {
263269 "/eel.js" : (_eel , dict ()),
264270 "/" : (_root , dict ()),
265271 "/<path:path>" : (_static , dict ()),
266272 "/eel" : (_websocket , dict (apply = [wbs .websocket ]))
267273}
268274
269- def register_eel_routes (app ) :
275+ def register_eel_routes (app : btl . Bottle ) -> None :
270276 '''
271277 Adds eel routes to `app`. Only needed if you are passing something besides `bottle.Bottle` to `eel.start()`.
272278 Ex:
@@ -281,11 +287,11 @@ def register_eel_routes(app):
281287
282288# Private functions
283289
284- def _safe_json (obj ) :
290+ def _safe_json (obj : Any ) -> str :
285291 return jsn .dumps (obj , default = lambda o : None )
286292
287293
288- def _repeated_send (ws , msg ):
294+ def _repeated_send (ws : WebSocket , msg : str ):
289295 for attempt in range (100 ):
290296 try :
291297 ws .send (msg )
@@ -294,7 +300,7 @@ def _repeated_send(ws, msg):
294300 sleep (0.001 )
295301
296302
297- def _process_message (message , ws ) :
303+ def _process_message (message : dict [ str , Any ], ws : WebSocket ) -> None :
298304 if 'call' in message :
299305 error_info = {}
300306 try :
@@ -326,47 +332,48 @@ def _process_message(message, ws):
326332 print ('Invalid message received: ' , message )
327333
328334
329- def _get_real_path (path ) :
335+ def _get_real_path (path : str ) -> str :
330336 if getattr (sys , 'frozen' , False ):
331- return os .path .join (sys ._MEIPASS , path )
337+ return os .path .join (sys ._MEIPASS , path ) # type: ignore # sys._MEIPASS is dynamically added by PyInstaller
332338 else :
333339 return os .path .abspath (path )
334340
335341
336- def _mock_js_function (f ) :
342+ def _mock_js_function (f : str ) -> None :
337343 exec ('%s = lambda *args: _mock_call("%s", args)' % (f , f ), globals ())
338344
339345
340- def _import_js_function (f ) :
346+ def _import_js_function (f : str ) -> None :
341347 exec ('%s = lambda *args: _js_call("%s", args)' % (f , f ), globals ())
342348
343349
344- def _call_object (name , args ) :
350+ def _call_object (name : str , args : Any ) -> Dict [ str , Any ] :
345351 global _call_number
346352 _call_number += 1
347353 call_id = _call_number + rnd .random ()
348354 return {'call' : call_id , 'name' : name , 'args' : args }
349355
350356
351- def _mock_call (name , args ) :
357+ def _mock_call (name : str , args : Any ) -> Callable [[ Optional [ Callable [..., Any ]], Optional [ Callable [..., Any ]]], Any ] :
352358 call_object = _call_object (name , args )
353359 global _mock_queue
354360 _mock_queue += [call_object ]
355361 return _call_return (call_object )
356362
357363
358- def _js_call (name , args ) :
364+ def _js_call (name : str , args : Any ) -> Callable [[ Optional [ Callable [..., Any ]], Optional [ Callable [..., Any ]]], Any ] :
359365 call_object = _call_object (name , args )
360366 for _ , ws in _websockets :
361367 _repeated_send (ws , _safe_json (call_object ))
362368 return _call_return (call_object )
363369
364370
365- def _call_return (call ) :
371+ def _call_return (call : dict [ str , Any ]) -> Callable [[ Optional [ Callable [..., Any ]], Optional [ Callable [..., Any ]]], Any ] :
366372 global _js_result_timeout
367373 call_id = call ['call' ]
368374
369- def return_func (callback = None , error_callback = None ):
375+ def return_func (callback : Optional [Callable [..., Any ]] = None ,
376+ error_callback : Optional [Callable [..., Any ]] = None ) -> Any :
370377 if callback is not None :
371378 _call_return_callbacks [call_id ] = (callback , error_callback )
372379 else :
@@ -377,18 +384,18 @@ def return_func(callback=None, error_callback=None):
377384 return return_func
378385
379386
380- def _expose (name , function ) :
387+ def _expose (name : str , function : Callable [..., Any ]) -> None :
381388 msg = 'Already exposed function with name "%s"' % name
382389 assert name not in _exposed_functions , msg
383390 _exposed_functions [name ] = function
384391
385392
386- def _detect_shutdown ():
393+ def _detect_shutdown () -> None :
387394 if len (_websockets ) == 0 :
388395 sys .exit ()
389396
390397
391- def _websocket_close (page ) :
398+ def _websocket_close (page : str ) -> None :
392399 global _shutdown
393400
394401 close_callback = _start_args .get ('close_callback' )
@@ -403,7 +410,7 @@ def _websocket_close(page):
403410 _shutdown = gvt .spawn_later (_start_args ['shutdown_delay' ], _detect_shutdown )
404411
405412
406- def _set_response_headers (response ):
413+ def _set_response_headers (response : btl . Response ):
407414 if _start_args ['disable_cache' ]:
408415 # https://stackoverflow.com/a/24748094/280852
409416 response .set_header ('Cache-Control' , 'no-store' )
0 commit comments