mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Password no longer echoed in getpass (in Windows). Fixes #1723
This commit is contained in:
parent
a08708dc8b
commit
90298c4e79
4 changed files with 129 additions and 40 deletions
|
|
@ -6,8 +6,11 @@ from _pydev_bundle._pydev_calltip_util import get_description
|
|||
from _pydev_imps._pydev_saved_modules import thread
|
||||
from _pydevd_bundle import pydevd_vars
|
||||
from _pydevd_bundle import pydevd_xml
|
||||
from _pydevd_bundle.pydevd_constants import IS_JYTHON, dict_iter_items, NEXT_VALUE_SEPARATOR, Null
|
||||
from _pydevd_bundle.pydevd_constants import (IS_JYTHON, dict_iter_items, NEXT_VALUE_SEPARATOR, Null,
|
||||
get_global_debugger)
|
||||
import signal
|
||||
from contextlib import contextmanager
|
||||
from _pydev_bundle import pydev_log
|
||||
|
||||
try:
|
||||
import cStringIO as StringIO # may not always be available @UnusedImport
|
||||
|
|
@ -109,31 +112,44 @@ class DebugConsoleStdIn(BaseStdIn):
|
|||
Object to be added to stdin (to emulate it as non-blocking while the next line arrives)
|
||||
'''
|
||||
|
||||
def __init__(self, dbg, original_stdin):
|
||||
def __init__(self, py_db, original_stdin):
|
||||
'''
|
||||
:param py_db:
|
||||
If None, get_global_debugger() is used.
|
||||
'''
|
||||
BaseStdIn.__init__(self, original_stdin)
|
||||
self.debugger = dbg
|
||||
self._py_db = py_db
|
||||
self._in_notification = 0
|
||||
|
||||
def __pydev_run_command(self, is_started):
|
||||
def __send_input_requested_message(self, is_started):
|
||||
try:
|
||||
cmd = self.debugger.cmd_factory.make_input_requested_message(is_started)
|
||||
self.debugger.writer.add_command(cmd)
|
||||
py_db = self._py_db
|
||||
if py_db is None:
|
||||
py_db = get_global_debugger()
|
||||
cmd = py_db.cmd_factory.make_input_requested_message(is_started)
|
||||
py_db.writer.add_command(cmd)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
return '\n'
|
||||
pydev_log.exception()
|
||||
|
||||
@contextmanager
|
||||
def notify_input_requested(self):
|
||||
self._in_notification += 1
|
||||
if self._in_notification == 1:
|
||||
self.__send_input_requested_message(True)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self._in_notification -= 1
|
||||
if self._in_notification == 0:
|
||||
self.__send_input_requested_message(False)
|
||||
|
||||
def readline(self, *args, **kwargs):
|
||||
# Notify Java side about input and call original function
|
||||
self.__pydev_run_command(True)
|
||||
result = self.original_stdin.readline(*args, **kwargs)
|
||||
self.__pydev_run_command(False)
|
||||
return result
|
||||
with self.notify_input_requested():
|
||||
return self.original_stdin.readline(*args, **kwargs)
|
||||
|
||||
def read(self, *args, **kwargs):
|
||||
# Notify Java side about input and call original function
|
||||
self.__pydev_run_command(True)
|
||||
result = self.original_stdin.read(*args, **kwargs)
|
||||
self.__pydev_run_command(False)
|
||||
return result
|
||||
with self.notify_input_requested():
|
||||
return self.original_stdin.read(*args, **kwargs)
|
||||
|
||||
|
||||
class CodeFragment:
|
||||
|
|
@ -220,7 +236,7 @@ class BaseInterpreterInterface:
|
|||
if debugger is None:
|
||||
return StdIn(self, self.host, self.client_port, original_stdin=original_std_in)
|
||||
else:
|
||||
return DebugConsoleStdIn(dbg=debugger, original_stdin=original_std_in)
|
||||
return DebugConsoleStdIn(py_db=debugger, original_stdin=original_std_in)
|
||||
|
||||
def add_exec(self, code_fragment, debugger=None):
|
||||
# In case sys.excepthook called, use original excepthook #PyDev-877: Debug console freezes with Python 3.5+
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ CONSOLE_ERROR = "error"
|
|||
class ConsoleMessage:
|
||||
"""Console Messages
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.more = False
|
||||
# List of tuple [('error', 'error_message'), ('message_list', 'output_message')]
|
||||
|
|
@ -62,15 +63,16 @@ class ConsoleMessage:
|
|||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DebugConsoleStdIn
|
||||
# _DebugConsoleStdIn
|
||||
#=======================================================================================================================
|
||||
class DebugConsoleStdIn(BaseStdIn):
|
||||
class _DebugConsoleStdIn(BaseStdIn):
|
||||
|
||||
@overrides(BaseStdIn.readline)
|
||||
def readline(self, *args, **kwargs):
|
||||
sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n')
|
||||
return '\n'
|
||||
|
||||
|
||||
#=======================================================================================================================
|
||||
# DebugConsole
|
||||
#=======================================================================================================================
|
||||
|
|
@ -87,8 +89,7 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
|
|||
except:
|
||||
pass
|
||||
|
||||
return DebugConsoleStdIn() #If buffered, raw_input is not supported in this console.
|
||||
|
||||
return _DebugConsoleStdIn() # If buffered, raw_input is not supported in this console.
|
||||
|
||||
@overrides(InteractiveConsole.push)
|
||||
def push(self, line, frame, buffer_output=True):
|
||||
|
|
@ -121,7 +122,7 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
|
|||
else:
|
||||
sys.stderr.write("Internal Error: %s\n" % (exc,))
|
||||
finally:
|
||||
#Remove frame references.
|
||||
# Remove frame references.
|
||||
self.frame = None
|
||||
frame = None
|
||||
if buffer_output:
|
||||
|
|
@ -133,12 +134,10 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
|
|||
else:
|
||||
return more, [], []
|
||||
|
||||
|
||||
@overrides(BaseInterpreterInterface.do_add_exec)
|
||||
def do_add_exec(self, line):
|
||||
return InteractiveConsole.push(self, line)
|
||||
|
||||
|
||||
@overrides(InteractiveConsole.runcode)
|
||||
def runcode(self, code):
|
||||
"""Execute a code object.
|
||||
|
|
@ -183,7 +182,7 @@ class InteractiveConsoleCache:
|
|||
interactive_console_instance = None
|
||||
|
||||
|
||||
#Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions.
|
||||
# Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions.
|
||||
def get_interactive_console(thread_id, frame_id, frame, console_message):
|
||||
"""returns the global interactive console.
|
||||
interactive console should have been initialized by this time
|
||||
|
|
@ -198,7 +197,7 @@ def get_interactive_console(thread_id, frame_id, frame, console_message):
|
|||
|
||||
console_stacktrace = traceback.extract_stack(frame, limit=1)
|
||||
if console_stacktrace:
|
||||
current_context = console_stacktrace[0] # top entry from stacktrace
|
||||
current_context = console_stacktrace[0] # top entry from stacktrace
|
||||
context_message = 'File "%s", line %s, in %s' % (current_context[0], current_context[1], current_context[2])
|
||||
console_message.add_console_message(CONSOLE_OUTPUT, "[Current context]: %s" % (context_message,))
|
||||
return InteractiveConsoleCache.interactive_console_instance
|
||||
|
|
@ -247,7 +246,3 @@ def get_completions(frame, act_tok):
|
|||
"""
|
||||
return _pydev_completer.generate_completions_as_xml(frame, act_tok)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import itertools
|
|||
import os
|
||||
import traceback
|
||||
import weakref
|
||||
import getpass as getpass_mod
|
||||
import functools
|
||||
|
||||
from _pydev_bundle import pydev_imports, pydev_log
|
||||
from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
|
||||
|
|
@ -27,6 +29,7 @@ from _pydevd_bundle import pydevd_extension_utils
|
|||
from _pydevd_bundle.pydevd_filtering import FilesFiltering
|
||||
from _pydevd_bundle import pydevd_io, pydevd_vm_type
|
||||
from _pydevd_bundle import pydevd_utils
|
||||
from _pydev_bundle.pydev_console_utils import DebugConsoleStdIn
|
||||
from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info
|
||||
from _pydevd_bundle.pydevd_breakpoints import ExceptionBreakpoint, get_exception_breakpoint
|
||||
from _pydevd_bundle.pydevd_comm_constants import (CMD_THREAD_SUSPEND, CMD_STEP_INTO, CMD_SET_BREAK,
|
||||
|
|
@ -36,7 +39,7 @@ from _pydevd_bundle.pydevd_comm_constants import (CMD_THREAD_SUSPEND, CMD_STEP_I
|
|||
from _pydevd_bundle.pydevd_constants import (IS_JYTH_LESS25, get_thread_id, get_current_thread_id,
|
||||
dict_keys, dict_iter_items, DebugInfoHolder, PYTHON_SUSPEND, STATE_SUSPEND, STATE_RUN, get_frame,
|
||||
clear_cached_thread_id, INTERACTIVE_MODE_AVAILABLE, SHOW_DEBUG_INFO_ENV, IS_PY34_OR_GREATER, IS_PY2, NULL,
|
||||
NO_FTRACE, IS_IRONPYTHON, JSON_PROTOCOL, IS_CPYTHON)
|
||||
NO_FTRACE, IS_IRONPYTHON, JSON_PROTOCOL, IS_CPYTHON, call_only_once)
|
||||
from _pydevd_bundle.pydevd_defaults import PydevdCustomization
|
||||
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
|
||||
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE, LIB_FILE
|
||||
|
|
@ -2250,7 +2253,7 @@ def _locked_settrace(
|
|||
if bufferStdErrToServer:
|
||||
init_stderr_redirect()
|
||||
|
||||
patch_stdin(debugger)
|
||||
patch_stdin()
|
||||
|
||||
t = threadingCurrentThread()
|
||||
additional_info = set_additional_thread_info(t)
|
||||
|
|
@ -2464,12 +2467,37 @@ def apply_debugger_options(setup_options):
|
|||
enable_qt_support(setup_options['qt-support'])
|
||||
|
||||
|
||||
def patch_stdin(debugger):
|
||||
from _pydev_bundle.pydev_console_utils import DebugConsoleStdIn
|
||||
orig_stdin = sys.stdin
|
||||
sys.stdin = DebugConsoleStdIn(debugger, orig_stdin)
|
||||
@call_only_once
|
||||
def patch_stdin():
|
||||
_internal_patch_stdin(None, sys, getpass_mod)
|
||||
|
||||
# Dispatch on_debugger_modules_loaded here, after all primary debugger modules are loaded
|
||||
|
||||
def _internal_patch_stdin(py_db=None, sys=None, getpass_mod=None):
|
||||
'''
|
||||
Note: don't use this function directly, use `patch_stdin()` instead.
|
||||
(this function is only meant to be used on test-cases to avoid patching the actual globals).
|
||||
'''
|
||||
# Patch stdin so that we notify when readline() is called.
|
||||
original_sys_stdin = sys.stdin
|
||||
debug_console_stdin = DebugConsoleStdIn(py_db, original_sys_stdin)
|
||||
sys.stdin = debug_console_stdin
|
||||
|
||||
_original_getpass = getpass_mod.getpass
|
||||
|
||||
@functools.wraps(_original_getpass)
|
||||
def getpass(*args, **kwargs):
|
||||
with DebugConsoleStdIn.notify_input_requested(debug_console_stdin):
|
||||
try:
|
||||
curr_stdin = sys.stdin
|
||||
if curr_stdin is debug_console_stdin:
|
||||
sys.stdin = original_sys_stdin
|
||||
return _original_getpass(*args, **kwargs)
|
||||
finally:
|
||||
sys.stdin = curr_stdin
|
||||
|
||||
getpass_mod.getpass = getpass
|
||||
|
||||
# Dispatch on_debugger_modules_loaded here, after all primary py_db modules are loaded
|
||||
|
||||
|
||||
for handler in pydevd_extension_utils.extensions_of_type(DebuggerEventHandler):
|
||||
|
|
@ -2606,7 +2634,7 @@ def main():
|
|||
pass
|
||||
|
||||
is_module = setup['module']
|
||||
patch_stdin(debugger)
|
||||
patch_stdin()
|
||||
|
||||
if setup['json-dap']:
|
||||
PyDevdAPI().set_protocol(debugger, 0, JSON_PROTOCOL)
|
||||
|
|
|
|||
|
|
@ -52,6 +52,50 @@ class _DummyPyDb(object):
|
|||
self.writer = _DummyWriter()
|
||||
|
||||
|
||||
def test_patch_stdin():
|
||||
from pydevd import _internal_patch_stdin
|
||||
|
||||
py_db = _DummyPyDb()
|
||||
|
||||
class _Stub(object):
|
||||
pass
|
||||
|
||||
actions = []
|
||||
|
||||
class OriginalStdin(object):
|
||||
|
||||
def readline(self):
|
||||
# On a readline we keep the patched version.
|
||||
assert sys_mod.stdin is not original_stdin
|
||||
actions.append('readline')
|
||||
return 'read'
|
||||
|
||||
def getpass_stub(*args, **kwargs):
|
||||
# On getpass we need to revert to the original version.
|
||||
actions.append('getpass')
|
||||
assert sys_mod.stdin is original_stdin
|
||||
return 'pass'
|
||||
|
||||
sys_mod = _Stub()
|
||||
original_stdin = sys_mod.stdin = OriginalStdin()
|
||||
|
||||
getpass_mod = _Stub()
|
||||
getpass_mod.getpass = getpass_stub
|
||||
|
||||
_internal_patch_stdin(py_db, sys_mod, getpass_mod)
|
||||
|
||||
assert sys_mod.stdin.readline() == 'read'
|
||||
|
||||
assert py_db.writer.command_meanings == ['CMD_INPUT_REQUESTED', 'CMD_INPUT_REQUESTED']
|
||||
del py_db.writer.command_meanings[:]
|
||||
assert actions == ['readline']
|
||||
del actions[:]
|
||||
|
||||
assert getpass_mod.getpass() == 'pass'
|
||||
assert py_db.writer.command_meanings == ['CMD_INPUT_REQUESTED', 'CMD_INPUT_REQUESTED']
|
||||
del py_db.writer.command_meanings[:]
|
||||
|
||||
|
||||
def test_debug_console():
|
||||
from _pydev_bundle.pydev_console_utils import DebugConsoleStdIn
|
||||
|
||||
|
|
@ -67,4 +111,10 @@ def test_debug_console():
|
|||
assert debug_console_std_in.readline() == 'read'
|
||||
|
||||
assert py_db.writer.command_meanings == ['CMD_INPUT_REQUESTED', 'CMD_INPUT_REQUESTED']
|
||||
del py_db.writer.command_meanings[:]
|
||||
|
||||
with debug_console_std_in.notify_input_requested():
|
||||
with debug_console_std_in.notify_input_requested():
|
||||
pass
|
||||
assert py_db.writer.command_meanings == ['CMD_INPUT_REQUESTED', 'CMD_INPUT_REQUESTED']
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue