1"""Utilities needed to emulate Python's interactive interpreter. 2 3""" 4 5# Inspired by similar code by Jeff Epler and Fredrik Lundh. 6 7 8import builtins 9import sys 10import traceback 11from codeop import CommandCompiler, compile_command 12 13__all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", 14 "compile_command"] 15 16 17class InteractiveInterpreter: 18 """Base class for InteractiveConsole. 19 20 This class deals with parsing and interpreter state (the user's 21 namespace); it doesn't deal with input buffering or prompting or 22 input file naming (the filename is always passed in explicitly). 23 24 """ 25 26 def __init__(self, locals=None): 27 """Constructor. 28 29 The optional 'locals' argument specifies a mapping to use as the 30 namespace in which code will be executed; it defaults to a newly 31 created dictionary with key "__name__" set to "__console__" and 32 key "__doc__" set to None. 33 34 """ 35 if locals is None: 36 locals = {"__name__": "__console__", "__doc__": None} 37 self.locals = locals 38 self.compile = CommandCompiler() 39 40 def runsource(self, source, filename="<input>", symbol="single"): 41 """Compile and run some source in the interpreter. 42 43 Arguments are as for compile_command(). 44 45 One of several things can happen: 46 47 1) The input is incorrect; compile_command() raised an 48 exception (SyntaxError or OverflowError). A syntax traceback 49 will be printed by calling the showsyntaxerror() method. 50 51 2) The input is incomplete, and more input is required; 52 compile_command() returned None. Nothing happens. 53 54 3) The input is complete; compile_command() returned a code 55 object. The code is executed by calling self.runcode() (which 56 also handles run-time exceptions, except for SystemExit). 57 58 The return value is True in case 2, False in the other cases (unless 59 an exception is raised). The return value can be used to 60 decide whether to use sys.ps1 or sys.ps2 to prompt the next 61 line. 62 63 """ 64 try: 65 code = self.compile(source, filename, symbol) 66 except (OverflowError, SyntaxError, ValueError): 67 # Case 1 68 self.showsyntaxerror(filename, source=source) 69 return False 70 71 if code is None: 72 # Case 2 73 return True 74 75 # Case 3 76 self.runcode(code) 77 return False 78 79 def runcode(self, code): 80 """Execute a code object. 81 82 When an exception occurs, self.showtraceback() is called to 83 display a traceback. All exceptions are caught except 84 SystemExit, which is reraised. 85 86 A note about KeyboardInterrupt: this exception may occur 87 elsewhere in this code, and may not always be caught. The 88 caller should be prepared to deal with it. 89 90 """ 91 try: 92 exec(code, self.locals) 93 except SystemExit: 94 raise 95 except: 96 self.showtraceback() 97 98 def showsyntaxerror(self, filename=None, **kwargs): 99 """Display the syntax error that just occurred. 100 101 This doesn't display a stack trace because there isn't one. 102 103 If a filename is given, it is stuffed in the exception instead 104 of what was there before (because Python's parser always uses 105 "<string>" when reading from a string). 106 107 The output is written by self.write(), below. 108 109 """ 110 try: 111 typ, value, tb = sys.exc_info() 112 if filename and issubclass(typ, SyntaxError): 113 value.filename = filename 114 source = kwargs.pop('source', "") 115 self._showtraceback(typ, value, None, source) 116 finally: 117 typ = value = tb = None 118 119 def showtraceback(self): 120 """Display the exception that just occurred. 121 122 We remove the first stack item because it is our own code. 123 124 The output is written by self.write(), below. 125 126 """ 127 try: 128 typ, value, tb = sys.exc_info() 129 self._showtraceback(typ, value, tb.tb_next, '') 130 finally: 131 typ = value = tb = None 132 133 def _showtraceback(self, typ, value, tb, source): 134 sys.last_type = typ 135 sys.last_traceback = tb 136 value = value.with_traceback(tb) 137 # Set the line of text that the exception refers to 138 lines = source.splitlines() 139 if (source and typ is SyntaxError 140 and not value.text and value.lineno is not None 141 and len(lines) >= value.lineno): 142 value.text = lines[value.lineno - 1] 143 sys.last_exc = sys.last_value = value = value.with_traceback(tb) 144 if sys.excepthook is sys.__excepthook__: 145 self._excepthook(typ, value, tb) 146 else: 147 # If someone has set sys.excepthook, we let that take precedence 148 # over self.write 149 try: 150 sys.excepthook(typ, value, tb) 151 except SystemExit: 152 raise 153 except BaseException as e: 154 e.__context__ = None 155 e = e.with_traceback(e.__traceback__.tb_next) 156 print('Error in sys.excepthook:', file=sys.stderr) 157 sys.__excepthook__(type(e), e, e.__traceback__) 158 print(file=sys.stderr) 159 print('Original exception was:', file=sys.stderr) 160 sys.__excepthook__(typ, value, tb) 161 162 def _excepthook(self, typ, value, tb): 163 # This method is being overwritten in 164 # _pyrepl.console.InteractiveColoredConsole 165 lines = traceback.format_exception(typ, value, tb) 166 self.write(''.join(lines)) 167 168 def write(self, data): 169 """Write a string. 170 171 The base implementation writes to sys.stderr; a subclass may 172 replace this with a different implementation. 173 174 """ 175 sys.stderr.write(data) 176 177 178class InteractiveConsole(InteractiveInterpreter): 179 """Closely emulate the behavior of the interactive Python interpreter. 180 181 This class builds on InteractiveInterpreter and adds prompting 182 using the familiar sys.ps1 and sys.ps2, and input buffering. 183 184 """ 185 186 def __init__(self, locals=None, filename="<console>", *, local_exit=False): 187 """Constructor. 188 189 The optional locals argument will be passed to the 190 InteractiveInterpreter base class. 191 192 The optional filename argument should specify the (file)name 193 of the input stream; it will show up in tracebacks. 194 195 """ 196 InteractiveInterpreter.__init__(self, locals) 197 self.filename = filename 198 self.local_exit = local_exit 199 self.resetbuffer() 200 201 def resetbuffer(self): 202 """Reset the input buffer.""" 203 self.buffer = [] 204 205 def interact(self, banner=None, exitmsg=None): 206 """Closely emulate the interactive Python console. 207 208 The optional banner argument specifies the banner to print 209 before the first interaction; by default it prints a banner 210 similar to the one printed by the real Python interpreter, 211 followed by the current class name in parentheses (so as not 212 to confuse this with the real interpreter -- since it's so 213 close!). 214 215 The optional exitmsg argument specifies the exit message 216 printed when exiting. Pass the empty string to suppress 217 printing an exit message. If exitmsg is not given or None, 218 a default message is printed. 219 220 """ 221 try: 222 sys.ps1 223 except AttributeError: 224 sys.ps1 = ">>> " 225 try: 226 sys.ps2 227 except AttributeError: 228 sys.ps2 = "... " 229 cprt = 'Type "help", "copyright", "credits" or "license" for more information.' 230 if banner is None: 231 self.write("Python %s on %s\n%s\n(%s)\n" % 232 (sys.version, sys.platform, cprt, 233 self.__class__.__name__)) 234 elif banner: 235 self.write("%s\n" % str(banner)) 236 more = 0 237 238 # When the user uses exit() or quit() in their interactive shell 239 # they probably just want to exit the created shell, not the whole 240 # process. exit and quit in builtins closes sys.stdin which makes 241 # it super difficult to restore 242 # 243 # When self.local_exit is True, we overwrite the builtins so 244 # exit() and quit() only raises SystemExit and we can catch that 245 # to only exit the interactive shell 246 247 _exit = None 248 _quit = None 249 250 if self.local_exit: 251 if hasattr(builtins, "exit"): 252 _exit = builtins.exit 253 builtins.exit = Quitter("exit") 254 255 if hasattr(builtins, "quit"): 256 _quit = builtins.quit 257 builtins.quit = Quitter("quit") 258 259 try: 260 while True: 261 try: 262 if more: 263 prompt = sys.ps2 264 else: 265 prompt = sys.ps1 266 try: 267 line = self.raw_input(prompt) 268 except EOFError: 269 self.write("\n") 270 break 271 else: 272 more = self.push(line) 273 except KeyboardInterrupt: 274 self.write("\nKeyboardInterrupt\n") 275 self.resetbuffer() 276 more = 0 277 except SystemExit as e: 278 if self.local_exit: 279 self.write("\n") 280 break 281 else: 282 raise e 283 finally: 284 # restore exit and quit in builtins if they were modified 285 if _exit is not None: 286 builtins.exit = _exit 287 288 if _quit is not None: 289 builtins.quit = _quit 290 291 if exitmsg is None: 292 self.write('now exiting %s...\n' % self.__class__.__name__) 293 elif exitmsg != '': 294 self.write('%s\n' % exitmsg) 295 296 def push(self, line, filename=None, _symbol="single"): 297 """Push a line to the interpreter. 298 299 The line should not have a trailing newline; it may have 300 internal newlines. The line is appended to a buffer and the 301 interpreter's runsource() method is called with the 302 concatenated contents of the buffer as source. If this 303 indicates that the command was executed or invalid, the buffer 304 is reset; otherwise, the command is incomplete, and the buffer 305 is left as it was after the line was appended. The return 306 value is 1 if more input is required, 0 if the line was dealt 307 with in some way (this is the same as runsource()). 308 309 """ 310 self.buffer.append(line) 311 source = "\n".join(self.buffer) 312 if filename is None: 313 filename = self.filename 314 more = self.runsource(source, filename, symbol=_symbol) 315 if not more: 316 self.resetbuffer() 317 return more 318 319 def raw_input(self, prompt=""): 320 """Write a prompt and read a line. 321 322 The returned line does not include the trailing newline. 323 When the user enters the EOF key sequence, EOFError is raised. 324 325 The base implementation uses the built-in function 326 input(); a subclass may replace this with a different 327 implementation. 328 329 """ 330 return input(prompt) 331 332 333class Quitter: 334 def __init__(self, name): 335 self.name = name 336 if sys.platform == "win32": 337 self.eof = 'Ctrl-Z plus Return' 338 else: 339 self.eof = 'Ctrl-D (i.e. EOF)' 340 341 def __repr__(self): 342 return f'Use {self.name} or {self.eof} to exit' 343 344 def __call__(self, code=None): 345 raise SystemExit(code) 346 347 348def interact(banner=None, readfunc=None, local=None, exitmsg=None, local_exit=False): 349 """Closely emulate the interactive Python interpreter. 350 351 This is a backwards compatible interface to the InteractiveConsole 352 class. When readfunc is not specified, it attempts to import the 353 readline module to enable GNU readline if it is available. 354 355 Arguments (all optional, all default to None): 356 357 banner -- passed to InteractiveConsole.interact() 358 readfunc -- if not None, replaces InteractiveConsole.raw_input() 359 local -- passed to InteractiveInterpreter.__init__() 360 exitmsg -- passed to InteractiveConsole.interact() 361 local_exit -- passed to InteractiveConsole.__init__() 362 363 """ 364 console = InteractiveConsole(local, local_exit=local_exit) 365 if readfunc is not None: 366 console.raw_input = readfunc 367 else: 368 try: 369 import readline 370 except ImportError: 371 pass 372 console.interact(banner, exitmsg) 373 374 375if __name__ == "__main__": 376 import argparse 377 378 parser = argparse.ArgumentParser() 379 parser.add_argument('-q', action='store_true', 380 help="don't print version and copyright messages") 381 args = parser.parse_args() 382 if args.q or sys.flags.quiet: 383 banner = '' 384 else: 385 banner = None 386 interact(banner) 387