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.
2
4
5
+ Utilities needed to emulate Python's interactive interpreter.
3
6
"""
4
7
5
8
# Inspired by similar code by Jeff Epler and Fredrik Lundh.
6
9
7
10
import sys
8
11
import traceback
12
+ import inspect
9
13
10
14
# START --------------------------- from codeop import CommandCompiler, compile_command
11
15
# START --------------------------- from codeop import CommandCompiler, compile_command
@@ -100,18 +104,21 @@ def _maybe_compile(compiler, source, filename, symbol):
100
104
101
105
try :
102
106
code1 = compiler (source + "\n " , filename , symbol )
103
- except SyntaxError as err1 :
104
- pass
107
+ except SyntaxError as e :
108
+ err1 = e
105
109
106
110
try :
107
111
code2 = compiler (source + "\n \n " , filename , symbol )
108
- except SyntaxError as err2 :
109
- pass
112
+ except SyntaxError as e :
113
+ err2 = e
110
114
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
115
122
116
123
117
124
def _compile (source , filename , symbol ):
@@ -148,6 +155,12 @@ class Compile:
148
155
def __init__ (self ):
149
156
self .flags = PyCF_DONT_IMPLY_DEDENT
150
157
158
+ try :
159
+ from ast import PyCF_ALLOW_TOP_LEVEL_AWAIT
160
+ self .flags |= PyCF_ALLOW_TOP_LEVEL_AWAIT
161
+ except :
162
+ pass
163
+
151
164
def __call__ (self , source , filename , symbol ):
152
165
codeob = compile (source , filename , symbol , self .flags , 1 )
153
166
for feature in _features :
@@ -197,19 +210,33 @@ def __call__(self, source, filename="<input>", symbol="single"):
197
210
__all__ = ["InteractiveInterpreter" , "InteractiveConsole" , "interact" ,
198
211
"compile_command" ]
199
212
213
+ from _pydev_bundle ._pydev_saved_modules import threading
200
214
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 ()
213
240
214
241
215
242
class InteractiveInterpreter :
@@ -240,7 +267,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
240
267
241
268
Arguments are as for compile_command().
242
269
243
- One several things can happen:
270
+ One of several things can happen:
244
271
245
272
1) The input is incorrect; compile_command() raised an
246
273
exception (SyntaxError or OverflowError). A syntax traceback
@@ -287,14 +314,24 @@ def runcode(self, code):
287
314
288
315
"""
289
316
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 )
291
331
except SystemExit :
292
332
raise
293
333
except :
294
334
self .showtraceback ()
295
- else :
296
- if softspace (sys .stdout , 0 ):
297
- sys .stdout .write ('\n ' )
298
335
299
336
def showsyntaxerror (self , filename = None ):
300
337
"""Display the syntax error that just occurred.
@@ -308,45 +345,49 @@ def showsyntaxerror(self, filename=None):
308
345
The output is written by self.write(), below.
309
346
310
347
"""
311
- type , value , sys . last_traceback = sys .exc_info ()
348
+ type , value , tb = sys .exc_info ()
312
349
sys .last_type = type
313
350
sys .last_value = value
351
+ sys .last_traceback = tb
314
352
if filename and type is SyntaxError :
315
353
# Work hard to stuff the correct filename in the exception
316
354
try :
317
- msg , (dummy_filename , lineno , offset , line ) = value
318
- except :
355
+ msg , (dummy_filename , lineno , offset , line ) = value . args
356
+ except ValueError :
319
357
# Not the format we expect; leave it alone
320
358
pass
321
359
else :
322
360
# Stuff in the right filename
323
361
value = SyntaxError (msg , (filename , lineno , offset , line ))
324
362
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 )
327
370
328
- def showtraceback (self , * args , ** kwargs ):
371
+ def showtraceback (self ):
329
372
"""Display the exception that just occurred.
330
373
331
374
We remove the first stack item because it is our own code.
332
375
333
376
The output is written by self.write(), below.
334
377
335
378
"""
379
+ sys .last_type , sys .last_value , last_tb = ei = sys .exc_info ()
380
+ sys .last_traceback = last_tb
336
381
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 )
347
389
finally :
348
- tblist = tb = None
349
- map (self .write , list )
390
+ last_tb = ei = None
350
391
351
392
def write (self , data ):
352
393
"""Write a string.
@@ -384,45 +425,46 @@ def resetbuffer(self):
384
425
"""Reset the input buffer."""
385
426
self .buffer = []
386
427
387
- def interact (self , banner = None ):
428
+ def interact (self , banner = None , exitmsg = None ):
388
429
"""Closely emulate the interactive Python console.
389
430
390
- The optional banner argument specify the banner to print
431
+ The optional banner argument specifies the banner to print
391
432
before the first interaction; by default it prints a banner
392
433
similar to the one printed by the real Python interpreter,
393
434
followed by the current class name in parentheses (so as not
394
435
to confuse this with the real interpreter -- since it's so
395
436
close!).
396
437
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
+
397
443
"""
398
444
try :
399
- sys .ps1 # @UndefinedVariable
445
+ sys .ps1
400
446
except AttributeError :
401
447
sys .ps1 = ">>> "
402
448
try :
403
- sys .ps2 # @UndefinedVariable
449
+ sys .ps2
404
450
except AttributeError :
405
451
sys .ps2 = "... "
406
452
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
407
453
if banner is None :
408
454
self .write ("Python %s on %s\n %s\n (%s)\n " %
409
455
(sys .version , sys .platform , cprt ,
410
456
self .__class__ .__name__ ))
411
- else :
457
+ elif banner :
412
458
self .write ("%s\n " % str (banner ))
413
459
more = 0
414
460
while 1 :
415
461
try :
416
462
if more :
417
- prompt = sys .ps2 # @UndefinedVariable
463
+ prompt = sys .ps2
418
464
else :
419
- prompt = sys .ps1 # @UndefinedVariable
465
+ prompt = sys .ps1
420
466
try :
421
467
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 )
426
468
except EOFError :
427
469
self .write ("\n " )
428
470
break
@@ -432,6 +474,10 @@ def interact(self, banner=None):
432
474
self .write ("\n KeyboardInterrupt\n " )
433
475
self .resetbuffer ()
434
476
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 )
435
481
436
482
def push (self , line ):
437
483
"""Push a line to the interpreter.
@@ -461,14 +507,14 @@ def raw_input(self, prompt=""):
461
507
When the user enters the EOF key sequence, EOFError is raised.
462
508
463
509
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
465
511
implementation.
466
512
467
513
"""
468
514
return input (prompt )
469
515
470
516
471
- def interact (banner = None , readfunc = None , local = None ):
517
+ def interact (banner = None , readfunc = None , local = None , exitmsg = None ):
472
518
"""Closely emulate the interactive Python interpreter.
473
519
474
520
This is a backwards compatible interface to the InteractiveConsole
@@ -480,6 +526,7 @@ def interact(banner=None, readfunc=None, local=None):
480
526
banner -- passed to InteractiveConsole.interact()
481
527
readfunc -- if not None, replaces InteractiveConsole.raw_input()
482
528
local -- passed to InteractiveInterpreter.__init__()
529
+ exitmsg -- passed to InteractiveConsole.interact()
483
530
484
531
"""
485
532
console = InteractiveConsole (local )
@@ -490,9 +537,18 @@ def interact(banner=None, readfunc=None, local=None):
490
537
import readline
491
538
except ImportError :
492
539
pass
493
- console .interact (banner )
540
+ console .interact (banner , exitmsg )
541
+
494
542
543
+ if __name__ == "__main__" :
544
+ import argparse
495
545
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