mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
Merge-request: IJ-MR-117377 Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com> GitOrigin-RevId: a2770b4c9eede71c732e251f1f1bfddcad4bd36b
573 lines
19 KiB
Python
573 lines
19 KiB
Python
'''
|
|
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(None, '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 <<every process in the process group of the
|
|
# calling process>> 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)
|