1- """Utilities needed to emulate Python's interactive interpreter.
1+ """
2+ A copy of the code module in the standard library with some changes to work with
3+ async evaluation.
24
5+ Utilities needed to emulate Python's interactive interpreter.
36"""
47
58# Inspired by similar code by Jeff Epler and Fredrik Lundh.
69
710import sys
811import traceback
12+ import inspect
913
1014# START --------------------------- from codeop import CommandCompiler, compile_command
1115# START --------------------------- from codeop import CommandCompiler, compile_command
@@ -100,18 +104,21 @@ def _maybe_compile(compiler, source, filename, symbol):
100104
101105 try :
102106 code1 = compiler (source + "\n " , filename , symbol )
103- except SyntaxError as err1 :
104- pass
107+ except SyntaxError as e :
108+ err1 = e
105109
106110 try :
107111 code2 = compiler (source + "\n \n " , filename , symbol )
108- except SyntaxError as err2 :
109- pass
112+ except SyntaxError as e :
113+ err2 = e
110114
111- if code :
112- return code
113- if not code1 and repr (err1 ) == repr (err2 ):
114- raise SyntaxError (err1 )
115+ try :
116+ if code :
117+ return code
118+ if not code1 and repr (err1 ) == repr (err2 ):
119+ raise err1
120+ finally :
121+ err1 = err2 = None
115122
116123
117124def _compile (source , filename , symbol ):
@@ -148,6 +155,12 @@ class Compile:
148155 def __init__ (self ):
149156 self .flags = PyCF_DONT_IMPLY_DEDENT
150157
158+ try :
159+ from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT
160+ self .flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT
161+ except :
162+ pass
163+
151164 def __call__ (self , source , filename , symbol ):
152165 codeob = compile (source , filename , symbol , self .flags , 1 )
153166 for feature in _features :
@@ -197,19 +210,33 @@ def __call__(self, source, filename="<input>", symbol="single"):
197210__all__ = ["InteractiveInterpreter" , "InteractiveConsole" , "interact" ,
198211 "compile_command" ]
199212
213+ from _pydev_bundle ._pydev_saved_modules import threading
200214
201- def softspace (file , newvalue ):
202- oldvalue = 0
203- try :
204- oldvalue = file .softspace
205- except AttributeError :
206- pass
207- try :
208- file .softspace = newvalue
209- except (AttributeError , TypeError ):
210- # "attribute-less object" or "read-only attributes"
211- pass
212- return oldvalue
215+
216+ class _EvalAwaitInNewEventLoop (threading .Thread ):
217+
218+ def __init__ (self , compiled , updated_globals , updated_locals ):
219+ threading .Thread .__init__ (self )
220+ self .daemon = True
221+ self ._compiled = compiled
222+ self ._updated_globals = updated_globals
223+ self ._updated_locals = updated_locals
224+
225+ # Output
226+ self .evaluated_value = None
227+ self .exc = None
228+
229+ async def _async_func (self ):
230+ return await eval (self ._compiled , self ._updated_locals , self ._updated_globals )
231+
232+ def run (self ):
233+ try :
234+ import asyncio
235+ loop = asyncio .new_event_loop ()
236+ asyncio .set_event_loop (loop )
237+ self .evaluated_value = asyncio .run (self ._async_func ())
238+ except :
239+ self .exc = sys .exc_info ()
213240
214241
215242class InteractiveInterpreter :
@@ -240,7 +267,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
240267
241268 Arguments are as for compile_command().
242269
243- One several things can happen:
270+ One of several things can happen:
244271
245272 1) The input is incorrect; compile_command() raised an
246273 exception (SyntaxError or OverflowError). A syntax traceback
@@ -287,14 +314,24 @@ def runcode(self, code):
287314
288315 """
289316 try :
290- exec (code , self .locals )
317+ is_async = False
318+ if hasattr (inspect , 'CO_COROUTINE' ):
319+ is_async = inspect .CO_COROUTINE & code .co_flags == inspect .CO_COROUTINE
320+
321+ if is_async :
322+ t = _EvalAwaitInNewEventLoop (code , self .locals , None )
323+ t .start ()
324+ t .join ()
325+
326+ if t .exc :
327+ raise t .exc [1 ].with_traceback (t .exc [2 ])
328+
329+ else :
330+ exec (code , self .locals )
291331 except SystemExit :
292332 raise
293333 except :
294334 self .showtraceback ()
295- else :
296- if softspace (sys .stdout , 0 ):
297- sys .stdout .write ('\n ' )
298335
299336 def showsyntaxerror (self , filename = None ):
300337 """Display the syntax error that just occurred.
@@ -308,45 +345,49 @@ def showsyntaxerror(self, filename=None):
308345 The output is written by self.write(), below.
309346
310347 """
311- type , value , sys . last_traceback = sys .exc_info ()
348+ type , value , tb = sys .exc_info ()
312349 sys .last_type = type
313350 sys .last_value = value
351+ sys .last_traceback = tb
314352 if filename and type is SyntaxError :
315353 # Work hard to stuff the correct filename in the exception
316354 try :
317- msg , (dummy_filename , lineno , offset , line ) = value
318- except :
355+ msg , (dummy_filename , lineno , offset , line ) = value . args
356+ except ValueError :
319357 # Not the format we expect; leave it alone
320358 pass
321359 else :
322360 # Stuff in the right filename
323361 value = SyntaxError (msg , (filename , lineno , offset , line ))
324362 sys .last_value = value
325- list = traceback .format_exception_only (type , value )
326- map (self .write , list )
363+ if sys .excepthook is sys .__excepthook__ :
364+ lines = traceback .format_exception_only (type , value )
365+ self .write ('' .join (lines ))
366+ else :
367+ # If someone has set sys.excepthook, we let that take precedence
368+ # over self.write
369+ sys .excepthook (type , value , tb )
327370
328- def showtraceback (self , * args , ** kwargs ):
371+ def showtraceback (self ):
329372 """Display the exception that just occurred.
330373
331374 We remove the first stack item because it is our own code.
332375
333376 The output is written by self.write(), below.
334377
335378 """
379+ sys .last_type , sys .last_value , last_tb = ei = sys .exc_info ()
380+ sys .last_traceback = last_tb
336381 try :
337- type , value , tb = sys .exc_info ()
338- sys .last_type = type
339- sys .last_value = value
340- sys .last_traceback = tb
341- tblist = traceback .extract_tb (tb )
342- del tblist [:1 ]
343- list = traceback .format_list (tblist )
344- if list :
345- list .insert (0 , "Traceback (most recent call last):\n " )
346- list [len (list ):] = traceback .format_exception_only (type , value )
382+ lines = traceback .format_exception (ei [0 ], ei [1 ], last_tb .tb_next )
383+ if sys .excepthook is sys .__excepthook__ :
384+ self .write ('' .join (lines ))
385+ else :
386+ # If someone has set sys.excepthook, we let that take precedence
387+ # over self.write
388+ sys .excepthook (ei [0 ], ei [1 ], last_tb )
347389 finally :
348- tblist = tb = None
349- map (self .write , list )
390+ last_tb = ei = None
350391
351392 def write (self , data ):
352393 """Write a string.
@@ -384,45 +425,46 @@ def resetbuffer(self):
384425 """Reset the input buffer."""
385426 self .buffer = []
386427
387- def interact (self , banner = None ):
428+ def interact (self , banner = None , exitmsg = None ):
388429 """Closely emulate the interactive Python console.
389430
390- The optional banner argument specify the banner to print
431+ The optional banner argument specifies the banner to print
391432 before the first interaction; by default it prints a banner
392433 similar to the one printed by the real Python interpreter,
393434 followed by the current class name in parentheses (so as not
394435 to confuse this with the real interpreter -- since it's so
395436 close!).
396437
438+ The optional exitmsg argument specifies the exit message
439+ printed when exiting. Pass the empty string to suppress
440+ printing an exit message. If exitmsg is not given or None,
441+ a default message is printed.
442+
397443 """
398444 try :
399- sys .ps1 # @UndefinedVariable
445+ sys .ps1
400446 except AttributeError :
401447 sys .ps1 = ">>> "
402448 try :
403- sys .ps2 # @UndefinedVariable
449+ sys .ps2
404450 except AttributeError :
405451 sys .ps2 = "... "
406452 cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
407453 if banner is None :
408454 self .write ("Python %s on %s\n %s\n (%s)\n " %
409455 (sys .version , sys .platform , cprt ,
410456 self .__class__ .__name__ ))
411- else :
457+ elif banner :
412458 self .write ("%s\n " % str (banner ))
413459 more = 0
414460 while 1 :
415461 try :
416462 if more :
417- prompt = sys .ps2 # @UndefinedVariable
463+ prompt = sys .ps2
418464 else :
419- prompt = sys .ps1 # @UndefinedVariable
465+ prompt = sys .ps1
420466 try :
421467 line = self .raw_input (prompt )
422- # Can be None if sys.stdin was redefined
423- encoding = getattr (sys .stdin , "encoding" , None )
424- if encoding and not isinstance (line , str ):
425- line = line .decode (encoding )
426468 except EOFError :
427469 self .write ("\n " )
428470 break
@@ -432,6 +474,10 @@ def interact(self, banner=None):
432474 self .write ("\n KeyboardInterrupt\n " )
433475 self .resetbuffer ()
434476 more = 0
477+ if exitmsg is None :
478+ self .write ('now exiting %s...\n ' % self .__class__ .__name__ )
479+ elif exitmsg != '' :
480+ self .write ('%s\n ' % exitmsg )
435481
436482 def push (self , line ):
437483 """Push a line to the interpreter.
@@ -461,14 +507,14 @@ def raw_input(self, prompt=""):
461507 When the user enters the EOF key sequence, EOFError is raised.
462508
463509 The base implementation uses the built-in function
464- raw_input (); a subclass may replace this with a different
510+ input (); a subclass may replace this with a different
465511 implementation.
466512
467513 """
468514 return input (prompt )
469515
470516
471- def interact (banner = None , readfunc = None , local = None ):
517+ def interact (banner = None , readfunc = None , local = None , exitmsg = None ):
472518 """Closely emulate the interactive Python interpreter.
473519
474520 This is a backwards compatible interface to the InteractiveConsole
@@ -480,6 +526,7 @@ def interact(banner=None, readfunc=None, local=None):
480526 banner -- passed to InteractiveConsole.interact()
481527 readfunc -- if not None, replaces InteractiveConsole.raw_input()
482528 local -- passed to InteractiveInterpreter.__init__()
529+ exitmsg -- passed to InteractiveConsole.interact()
483530
484531 """
485532 console = InteractiveConsole (local )
@@ -490,9 +537,18 @@ def interact(banner=None, readfunc=None, local=None):
490537 import readline
491538 except ImportError :
492539 pass
493- console .interact (banner )
540+ console .interact (banner , exitmsg )
541+
494542
543+ if __name__ == "__main__" :
544+ import argparse
495545
496- if __name__ == '__main__' :
497- import pdb
498- pdb .run ("interact()\n " )
546+ parser = argparse .ArgumentParser ()
547+ parser .add_argument ('-q' , action = 'store_true' ,
548+ help = "don't print version and copyright messages" )
549+ args = parser .parse_args ()
550+ if args .q or sys .flags .quiet :
551+ banner = ''
552+ else :
553+ banner = None
554+ interact (banner )
0 commit comments