''' Entry point module to start the interactive console. ''' from _pydev_bundle._pydev_getopt import gnu_getopt from _pydev_comm.pydev_rpc import make_rpc_client, start_rpc_server, start_rpc_server_and_make_client from _pydev_imps._pydev_saved_modules import thread start_new_thread = thread.start_new_thread try: from code import InteractiveConsole except ImportError: from _pydevd_bundle.pydevconsole_code_for_ironpython import IronPythonInteractiveConsole as InteractiveConsole import os import sys from _pydev_imps._pydev_saved_modules import threading from _pydevd_bundle.pydevd_constants import INTERACTIVE_MODE_AVAILABLE, dict_keys, IS_ASYNCIO_REPL from _pydevd_bundle.pydevd_utils import save_main_module from _pydev_bundle import fix_getpass fix_getpass.fix_getpass() from _pydev_bundle.pydev_imports import _queue try: import __builtin__ except: import builtins as __builtin__ # @UnresolvedImport from _pydev_bundle.pydev_stdin import BaseStdIn from _pydev_bundle.pydev_console_utils import BaseInterpreterInterface from _pydev_bundle.pydev_console_types import Command IS_PYTHON_3_ONWARDS = sys.version_info[0] >= 3 IS_PY24 = sys.version_info[0] == 2 and sys.version_info[1] == 4 try: try: execfile #Not in Py3k except NameError: from _pydev_bundle.pydev_imports import execfile __builtin__.execfile = execfile except: pass # Pull in runfile, the interface to UMD that wraps execfile from _pydev_bundle.pydev_umd import runfile, _set_globals_function if sys.version_info[0] >= 3: import builtins # @UnresolvedImport builtins.runfile = runfile else: import __builtin__ __builtin__.runfile = runfile #======================================================================================================================= # InterpreterInterface #======================================================================================================================= class InterpreterInterface(BaseInterpreterInterface): ''' The methods in this class should be registered in the xml-rpc server. ''' def __init__(self, mainThread, connect_status_queue=None, rpc_client=None): BaseInterpreterInterface.__init__(self, mainThread, connect_status_queue, rpc_client) self.namespace = {} self.save_main() if AsyncioInteractiveConsole is not None: self.interpreter = AsyncioInteractiveConsole(self.namespace) else: self.interpreter = InteractiveConsole(self.namespace) self._input_error_printed = False def save_main(self): m = save_main_module('', 'pydevconsole') self.namespace = m.__dict__ try: self.namespace['__builtins__'] = __builtins__ except NameError: pass # Not there on Jython... def do_add_exec(self, codeFragment): command = Command(self.interpreter, codeFragment) # doesn't work correctly in python version < 3 if sys.version_info < (3,): command.run() return command.more, False with CommandExceptionManager(command): command.run() return command.more, command.exception_occurred def get_namespace(self): return self.namespace def close(self): sys.exit(0) class CommandExceptionManager: def __init__(self, cls): self.original_hook = sys.excepthook self.command = cls def __enter__(self): def info(type, value, tb): self.command.exception_occurred = True if (not sys.stderr.isatty() or not sys.stdin.isatty()): self.original_hook(type, value, tb) else: import traceback;traceback.print_exception(type, value, tb) sys.excepthook = info return self def __exit__(self, exc_type, exc_val, exc_tb): sys.excepthook = self.original_hook class _ProcessExecQueueHelper: _debug_hook = None _return_control_osc = False def set_debug_hook(debug_hook): _ProcessExecQueueHelper._debug_hook = debug_hook def activate_mpl_if_already_imported(interpreter): if interpreter.mpl_modules_for_patching: for module in dict_keys(interpreter.mpl_modules_for_patching): if module in sys.modules: activate_function = interpreter.mpl_modules_for_patching.pop(module) activate_function() def init_set_return_control_back(interpreter): from pydev_ipython.inputhook import set_return_control_callback def return_control(): ''' A function that the inputhooks can call (via inputhook.stdin_ready()) to find out if they should cede control and return ''' if _ProcessExecQueueHelper._debug_hook: # Some of the input hooks check return control without doing # a single operation, so we don't return True on every # call when the debug hook is in place to allow the GUI to run # XXX: Eventually the inputhook code will have diverged enough # from the IPython source that it will be worthwhile rewriting # it rather than pretending to maintain the old API _ProcessExecQueueHelper._return_control_osc = not _ProcessExecQueueHelper._return_control_osc if _ProcessExecQueueHelper._return_control_osc: return True if not interpreter.exec_queue.empty(): return True return False set_return_control_callback(return_control) def init_mpl_in_console(interpreter): init_set_return_control_back(interpreter) if not INTERACTIVE_MODE_AVAILABLE: return activate_mpl_if_already_imported(interpreter) from _pydev_bundle.pydev_import_hook import import_hook_manager for mod in dict_keys(interpreter.mpl_modules_for_patching): import_hook_manager.add_module_name(mod, interpreter.mpl_modules_for_patching.pop(mod)) if sys.platform != 'win32': def pid_exists(pid): # Note that this function in the face of errors will conservatively consider that # the pid is still running (because we'll exit the current process when it's # no longer running, so, we need to be 100% sure it actually exited). import errno if pid == 0: # According to "man 2 kill" PID 0 has a special meaning: # it refers to <> so we don't want to go any further. # If we get here it means this UNIX platform *does* have # a process with id 0. return True try: os.kill(pid, 0) except OSError as err: if err.errno == errno.ESRCH: # ESRCH == No such process return False elif err.errno == errno.EPERM: # EPERM clearly means there's a process to deny access to return True else: # According to "man 2 kill" possible error values are # (EINVAL, EPERM, ESRCH) therefore we should never get # here. If we do, although it's an error, consider it # exists (see first comment in this function). return True else: return True else: def pid_exists(pid): # Note that this function in the face of errors will conservatively consider that # the pid is still running (because we'll exit the current process when it's # no longer running, so, we need to be 100% sure it actually exited). import ctypes kernel32 = ctypes.windll.kernel32 PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 ERROR_INVALID_PARAMETER = 0x57 STILL_ACTIVE = 259 process = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) if not process: err = kernel32.GetLastError() if err == ERROR_INVALID_PARAMETER: # Means it doesn't exist (pid parameter is wrong). return False # There was some unexpected error (such as access denied), so # consider it exists (although it could be something else, but we don't want # to raise any errors -- so, just consider it exists). return True try: zero = ctypes.c_int(0) exit_code = ctypes.pointer(zero) exit_code_suceeded = kernel32.GetExitCodeProcess(process, exit_code) if not exit_code_suceeded: # There was some unexpected error (such as access denied), so # consider it exists (although it could be something else, but we don't want # to raise any errors -- so, just consider it exists). return True elif bool(exit_code.contents.value) and int(exit_code.contents.value) != STILL_ACTIVE: return False finally: kernel32.CloseHandle(process) return True def process_exec_queue(interpreter): init_mpl_in_console(interpreter) from pydev_ipython.inputhook import get_inputhook try: kill_if_pid_not_alive = int(os.environ.get('PYDEV_ECLIPSE_PID', '-1')) except: kill_if_pid_not_alive = -1 while 1: if kill_if_pid_not_alive != -1: if not pid_exists(kill_if_pid_not_alive): exit() # Running the request may have changed the inputhook in use inputhook = get_inputhook() if _ProcessExecQueueHelper._debug_hook: _ProcessExecQueueHelper._debug_hook() if inputhook: try: # Note: it'll block here until return_control returns True. inputhook() except: import traceback;traceback.print_exc() try: try: code_fragment = interpreter.exec_queue.get(block=True, timeout=1/20.) # 20 calls/second except _queue.Empty: continue if hasattr(code_fragment, '__call__'): # It can be a callable (i.e.: something that must run in the main # thread can be put in the queue for later execution). code_fragment() else: interpreter.add_exec(code_fragment) except KeyboardInterrupt: interpreter.buffer = None continue except SystemExit: raise except: type, value, tb = sys.exc_info() import traceback;traceback.print_exception(type, value, tb, file=sys.__stderr__) exit() if 'IPYTHONENABLE' in os.environ: IPYTHON = os.environ['IPYTHONENABLE'] == 'True' else: IPYTHON = True try: try: exitfunc = sys.exitfunc except AttributeError: exitfunc = None if IPYTHON: from _pydev_bundle.pydev_ipython_console import IPythonInterpreterInterface as InterpreterInterface if exitfunc is not None: sys.exitfunc = exitfunc else: try: delattr(sys, 'exitfunc') except: pass except: IPYTHON = False pass AsyncioInteractiveConsole = None if IS_ASYNCIO_REPL and not IPYTHON: import asyncio import ast import types import inspect def create_new_loop(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) def create_task(coro, name=None): try: loop = asyncio.get_event_loop() except RuntimeError: create_new_loop() loop = asyncio.get_event_loop() task = loop.create_task(coro) if name is not None: task.set_name(name) return task asyncio.create_task = create_task class _AsyncioInteractiveConsole(InteractiveConsole): """ Simulates asyncio REPL (python -m asyncio) """ def get_event_loop_err(self): return 'There is no current event loop in thread %r.' % threading.current_thread().name def __init__(self, locals): super(_AsyncioInteractiveConsole, self).__init__(locals) self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT def runcode(self, code): try: func = types.FunctionType(code, self.locals) coro = func() if inspect.iscoroutine(coro): loop = asyncio.get_event_loop() loop.run_until_complete(coro) except SystemExit: raise except RuntimeError as err: if str(err) == self.get_event_loop_err(): create_new_loop() self.runcode(code) else: self.showtraceback() except: self.showtraceback() AsyncioInteractiveConsole = _AsyncioInteractiveConsole #======================================================================================================================= # _DoExit #======================================================================================================================= def do_exit(*args): ''' We have to override the exit because calling sys.exit will only actually exit the main thread, and as we're in a Xml-rpc server, that won't work. ''' try: import java.lang.System java.lang.System.exit(1) except ImportError: if len(args) == 1: os._exit(args[0]) else: os._exit(0) def enable_thrift_logging(): """Sets up `thriftpy` logger The logger is used in `thriftpy/server.py` for logging exceptions. """ import logging # create logger logger = logging.getLogger('_shaded_thriftpy') logger.setLevel(logging.DEBUG) # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # add formatter to ch ch.setFormatter(formatter) # add ch to logger logger.addHandler(ch) def create_server_handler_factory(interpreter): def server_handler_factory(rpc_client): interpreter.rpc_client = rpc_client return interpreter return server_handler_factory def start_server(port): if port is None: port = 0 # 0. General stuff #replace exit (see comments on method) #note that this does not work in jython!!! (sys method can't be replaced). sys.exit = do_exit from pydev_console.pydev_protocol import PythonConsoleBackendService, PythonConsoleFrontendService enable_thrift_logging() server_service = PythonConsoleBackendService client_service = PythonConsoleFrontendService # 1. Start Python console server # `InterpreterInterface` implements all methods required for `server_handler` interpreter = InterpreterInterface(threading.current_thread()) # Tell UMD the proper default namespace _set_globals_function(interpreter.get_namespace) server_socket = start_rpc_server_and_make_client('', port, server_service, client_service, create_server_handler_factory(interpreter)) # 2. Print server port for the IDE _, server_port = server_socket.getsockname() print(server_port) # 3. Wait for IDE to connect to the server process_exec_queue(interpreter) def start_client(host, port): #replace exit (see comments on method) #note that this does not work in jython!!! (sys method can't be replaced). sys.exit = do_exit from pydev_console.pydev_protocol import PythonConsoleBackendService, PythonConsoleFrontendService enable_thrift_logging() client_service = PythonConsoleFrontendService client, server_transport = make_rpc_client(client_service, host, port) interpreter = InterpreterInterface(threading.current_thread(), rpc_client=client) # we do not need to start the server in a new thread because it does not need to accept a client connection, it already has it # Tell UMD the proper default namespace _set_globals_function(interpreter.get_namespace) server_service = PythonConsoleBackendService # `InterpreterInterface` implements all methods required for the handler server_handler = interpreter start_rpc_server(server_transport, server_service, server_handler) process_exec_queue(interpreter) def get_ipython_hidden_vars(): if IPYTHON and hasattr(__builtin__, 'interpreter'): interpreter = get_interpreter() return interpreter.get_ipython_hidden_vars_dict() else: try: ipython_shell = get_ipython() from _pydev_bundle.pydev_ipython_console_011 import get_ipython_hidden_vars return get_ipython_hidden_vars(ipython_shell) except: pass def get_interpreter(): try: interpreterInterface = getattr(__builtin__, 'interpreter') except AttributeError: interpreterInterface = InterpreterInterface(None, None, threading.current_thread()) __builtin__.interpreter = interpreterInterface print(interpreterInterface.get_greeting_msg()) return interpreterInterface def get_completions(text, token, globals, locals): interpreterInterface = get_interpreter() interpreterInterface.interpreter.update(globals, locals) return interpreterInterface.getCompletions(text, token) #======================================================================================================================= # main #======================================================================================================================= if __name__ == '__main__': #Important: don't use this module directly as the __main__ module, rather, import itself as pydevconsole #so that we don't get multiple pydevconsole modules if it's executed directly (otherwise we'd have multiple #representations of its classes). #See: https://sw-brainwy.rhcloud.com/tracker/PyDev/446: #'Variables' and 'Expressions' views stopped working when debugging interactive console import pydevconsole sys.stdin = pydevconsole.BaseStdIn(sys.stdin) # parse command-line arguments optlist, _ = gnu_getopt(sys.argv, 'm:h:p', ['mode=', 'host=', 'port=']) mode = None host = None port = None for opt, arg in optlist: if opt in ('-m', '--mode'): mode = arg elif opt in ('-h', '--host'): host = arg elif opt in ('-p', '--port'): port = int(arg) if mode not in ('client', 'server'): sys.exit(-1) if mode == 'client': if not port: # port must be set for client sys.exit(-1) if not host: from _pydev_bundle import pydev_localhost host = client_host = pydev_localhost.get_localhost() pydevconsole.start_client(host, port) elif mode == 'server': pydevconsole.start_server(port)