2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python.debugger;
18 import com.google.common.base.Strings;
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps;
21 import com.intellij.execution.process.ProcessEvent;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.process.ProcessListener;
24 import com.intellij.execution.ui.ConsoleView;
25 import com.intellij.execution.ui.ConsoleViewContentType;
26 import com.intellij.execution.ui.ExecutionConsole;
27 import com.intellij.openapi.actionSystem.AnActionEvent;
28 import com.intellij.openapi.actionSystem.DefaultActionGroup;
29 import com.intellij.openapi.actionSystem.Presentation;
30 import com.intellij.openapi.actionSystem.ToggleAction;
31 import com.intellij.openapi.application.AccessToken;
32 import com.intellij.openapi.application.ApplicationInfo;
33 import com.intellij.openapi.application.ApplicationManager;
34 import com.intellij.openapi.diagnostic.Logger;
35 import com.intellij.openapi.editor.Document;
36 import com.intellij.openapi.extensions.Extensions;
37 import com.intellij.openapi.fileEditor.FileDocumentManager;
38 import com.intellij.openapi.module.Module;
39 import com.intellij.openapi.module.ModuleUtilCore;
40 import com.intellij.openapi.progress.ProgressIndicator;
41 import com.intellij.openapi.progress.ProgressManager;
42 import com.intellij.openapi.progress.Task;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.ui.Messages;
45 import com.intellij.openapi.util.Key;
46 import com.intellij.openapi.util.Ref;
47 import com.intellij.openapi.vfs.VirtualFile;
48 import com.intellij.psi.PsiElement;
49 import com.intellij.psi.PsiFile;
50 import com.intellij.psi.PsiManager;
51 import com.intellij.psi.ResolveState;
52 import com.intellij.psi.scope.PsiScopeProcessor;
53 import com.intellij.psi.util.PsiTreeUtil;
54 import com.intellij.remote.RemoteProcessControl;
55 import com.intellij.util.ui.UIUtil;
56 import com.intellij.xdebugger.*;
57 import com.intellij.xdebugger.breakpoints.*;
58 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
59 import com.intellij.xdebugger.frame.XExecutionStack;
60 import com.intellij.xdebugger.frame.XStackFrame;
61 import com.intellij.xdebugger.frame.XSuspendContext;
62 import com.intellij.xdebugger.frame.XValueChildrenList;
63 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
64 import com.jetbrains.python.PythonFileType;
65 import com.jetbrains.python.console.PythonConsoleView;
66 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
67 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
68 import com.jetbrains.python.debugger.containerview.PyViewNumericContainerAction;
69 import com.jetbrains.python.debugger.pydev.*;
70 import com.jetbrains.python.debugger.settings.PyDebuggerSettings;
71 import com.jetbrains.python.psi.*;
72 import com.jetbrains.python.psi.resolve.PyResolveContext;
73 import com.jetbrains.python.psi.resolve.PyResolveUtil;
74 import com.jetbrains.python.psi.resolve.RatedResolveResult;
75 import com.jetbrains.python.psi.types.PyClassType;
76 import com.jetbrains.python.psi.types.PyModuleType;
77 import com.jetbrains.python.psi.types.PyType;
78 import com.jetbrains.python.psi.types.PyTypeParser;
79 import org.jetbrains.annotations.NotNull;
80 import org.jetbrains.annotations.Nullable;
82 import java.io.IOException;
83 import java.net.ServerSocket;
85 import java.util.concurrent.ConcurrentHashMap;
87 import static javax.swing.SwingUtilities.invokeLater;
92 // todo: bundle messages
93 // todo: pydevd supports module reloading - look for a way to use the feature
94 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
96 private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.PyDebugProcess");
97 private static final int CONNECTION_TIMEOUT = 60000;
99 private final ProcessDebugger myDebugger;
100 private final XBreakpointHandler[] myBreakpointHandlers;
101 private final PyDebuggerEditorsProvider myEditorsProvider;
102 private final ProcessHandler myProcessHandler;
103 private final ExecutionConsole myExecutionConsole;
104 private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<>();
105 private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
106 new ConcurrentHashMap<>();
108 private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
109 private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
110 private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
111 private boolean myDownloadSources = false;
113 private boolean myClosing = false;
115 private PyPositionConverter myPositionConverter;
116 private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
117 private boolean myWaitingForConnection = false;
118 private PyStackFrame myConsoleContextFrame = null;
119 private PyReferrersLoader myReferrersProvider;
121 public PyDebugProcess(@NotNull XDebugSession session,
122 @NotNull ServerSocket serverSocket,
123 @NotNull ExecutionConsole executionConsole,
124 @Nullable ProcessHandler processHandler, boolean multiProcess) {
125 this(session, multiProcess ? process -> process.createMultiprocessDebugger(serverSocket)
126 : process -> new RemoteDebugger(process, serverSocket, process.getConnectTimeout()),
127 executionConsole, processHandler);
130 public PyDebugProcess(final @NotNull XDebugSession session,
131 @NotNull final ExecutionConsole executionConsole,
132 @Nullable final ProcessHandler processHandler,
133 @NotNull String serverHost, int serverPort) {
134 this(session, process -> new ClientModeMultiProcessDebugger(process, serverHost, serverPort), executionConsole, processHandler);
137 private PyDebugProcess(@NotNull XDebugSession session,
138 @NotNull DebuggerFactory debuggerFactory,
139 @NotNull ExecutionConsole executionConsole,
140 @Nullable ProcessHandler processHandler) {
143 session.setPauseActionSupported(true);
145 myDebugger = debuggerFactory.createDebugger(this);
147 List<XBreakpointHandler> breakpointHandlers = new ArrayList<>();
148 breakpointHandlers.add(new PyLineBreakpointHandler(this));
149 breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
150 for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
151 breakpointHandlers.add(factory.createBreakpointHandler(this));
153 myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
155 myEditorsProvider = new PyDebuggerEditorsProvider();
156 mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
157 myProcessHandler = processHandler;
158 myExecutionConsole = executionConsole;
159 if (myProcessHandler != null) {
160 myProcessHandler.addProcessListener(this);
162 if (processHandler instanceof PositionConverterProvider) {
163 myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
166 myPositionConverter = new PyLocalPositionConverter();
168 myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
170 public void closed() {
175 public void communicationError() {
176 detachDebuggedProcess();
180 public void detached() {
181 detachDebuggedProcess();
185 session.addSessionListener(new XDebugSessionListener() {
187 public void stackFrameChanged() {
188 String currentFrameThreadId = null;
189 final XStackFrame currentFrame = session.getCurrentStackFrame();
190 if (currentFrame instanceof PyStackFrame) {
191 currentFrameThreadId = ((PyStackFrame)currentFrame).getThreadId();
193 final XExecutionStack activeStack = session.getSuspendContext().getActiveExecutionStack();
194 if ((activeStack == null) || (currentFrameThreadId == null)) {
197 final XStackFrame frameFromSuspendContext = activeStack.getTopFrame();
198 String activeStackThreadId = null;
199 if (frameFromSuspendContext instanceof PyStackFrame) {
200 activeStackThreadId = ((PyStackFrame)frameFromSuspendContext).getThreadId();
202 if (!currentFrameThreadId.equals(activeStackThreadId)) {
203 // another thread was selected, we should update suspendContext
204 PyThreadInfo threadInfo = null;
205 for (PyThreadInfo info : mySuspendedThreads) {
206 if (info.getId().equals(currentFrameThreadId)) {
211 if (threadInfo != null) {
212 getSession().positionReached(createSuspendContext(threadInfo));
219 private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
220 MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
221 debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
223 public void threadsClosed(Set<String> threadIds) {
224 for (PyThreadInfo t : mySuspendedThreads) {
225 if (threadIds.contains(t.getId())) {
226 if (getSession().isSuspended()) {
227 getSession().resume();
237 protected void detachDebuggedProcess() {
238 handleStop(); //in case of normal debug we stop the session
241 protected void handleStop() {
245 public void setPositionConverter(PyPositionConverter positionConverter) {
246 myPositionConverter = positionConverter;
251 public PyPositionConverter getPositionConverter() {
252 return myPositionConverter;
257 public XBreakpointHandler<?>[] getBreakpointHandlers() {
258 return myBreakpointHandlers;
263 public XDebuggerEditorsProvider getEditorsProvider() {
264 return myEditorsProvider;
269 protected ProcessHandler doGetProcessHandler() {
270 return myProcessHandler;
275 public ExecutionConsole createConsole() {
276 return myExecutionConsole;
280 public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
281 return mySmartStepIntoHandler;
285 public void sessionInitialized() {
286 waitForConnection(getConnectionMessage(), getConnectionTitle());
289 protected void waitForConnection(final String connectionMessage, String connectionTitle) {
290 ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
292 public void run(@NotNull final ProgressIndicator indicator) {
293 indicator.setText(connectionMessage);
296 myWaitingForConnection = true;
297 myDebugger.waitForConnect();
298 myWaitingForConnection = false;
305 catch (final Exception e) {
306 myWaitingForConnection = false;
307 if (myProcessHandler != null) {
308 myProcessHandler.destroyProcess();
312 () -> Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle()));
321 getSession().rebuildViews();
322 registerBreakpoints();
323 setShowReturnValues(PyDebuggerSettings.getInstance().isWatchReturnValues());
327 public int handleDebugPort(int localPort) throws IOException {
328 if (myProcessHandler instanceof RemoteProcessControl) {
329 return getRemoteTunneledPort(localPort, (RemoteProcessControl)myProcessHandler);
336 protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessControl handler) throws IOException {
338 return handler.getRemoteSocket(localPort).getSecond();
340 catch (Exception e) {
341 throw new IOException(e);
346 public void recordSignature(PySignature signature) {
347 PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
351 public void recordLogEvent(PyConcurrencyEvent event) {
352 PyConcurrencyService.getInstance(getSession().getProject()).recordEvent(getSession(), event, event.isAsyncio());
356 public void showConsole(PyThreadInfo thread) {
357 myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
358 if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
359 PythonDebugLanguageConsoleView consoleView = (PythonDebugLanguageConsoleView)myExecutionConsole;
360 UIUtil.invokeLaterIfNeeded(() -> {
361 consoleView.enableConsole(false);
362 consoleView.getPydevConsoleView().setConsoleEnabled(true);
368 public void consoleInputRequested(boolean isStarted) {
369 if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
370 PythonConsoleView consoleView = ((PythonDebugLanguageConsoleView)myExecutionConsole).getPydevConsoleView();
372 consoleView.inputRequested();
375 consoleView.inputReceived();
380 protected void afterConnect() {
383 protected void beforeConnect() {
386 protected String getConnectionMessage() {
387 return "Waiting for connection...";
390 protected String getConnectionTitle() {
391 return "Connecting To Debugger";
394 private void handshake() throws PyDebuggerException {
395 String remoteVersion = myDebugger.handshake();
396 String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
397 if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
398 remoteVersion = currentBuild;
400 else if (remoteVersion.startsWith("PY-")) {
401 remoteVersion = remoteVersion.substring(3);
404 remoteVersion = null;
406 printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
408 if (remoteVersion != null) {
409 if (!(remoteVersion.equals(currentBuild) || remoteVersion.startsWith(currentBuild))) {
410 LOG.warn(String.format("Wrong debugger version. Remote version: %s Current build: %s", remoteVersion, currentBuild));
411 printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
412 ConsoleViewContentType.ERROR_OUTPUT);
418 public void printToConsole(String text, ConsoleViewContentType contentType) {
419 ((ConsoleView)myExecutionConsole).print(text, contentType);
422 private void registerBreakpoints() {
423 registerLineBreakpoints();
424 registerExceptionBreakpoints();
427 private void registerExceptionBreakpoints() {
428 for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
429 addExceptionBreakpoint(bp);
433 public void registerLineBreakpoints() {
434 for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
435 addBreakpoint(entry.getKey(), entry.getValue());
440 public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar,
441 @NotNull DefaultActionGroup topToolbar,
442 @NotNull DefaultActionGroup settings) {
443 super.registerAdditionalActions(leftToolbar, topToolbar, settings);
444 settings.add(new WatchReturnValuesAction(this));
445 settings.add(new SimplifiedView(this));
448 private static class WatchReturnValuesAction extends ToggleAction {
449 private volatile boolean myWatchesReturnValues;
450 private final PyDebugProcess myProcess;
451 private final String myText;
453 public WatchReturnValuesAction(@NotNull PyDebugProcess debugProcess) {
454 super("", "Enables watching executed functions return values", null);
455 myWatchesReturnValues = PyDebuggerSettings.getInstance().isWatchReturnValues();
456 myProcess = debugProcess;
457 myText = "Show Return Values";
461 public void update(@NotNull final AnActionEvent e) {
463 final Presentation presentation = e.getPresentation();
464 presentation.setEnabled(true);
465 presentation.setText(myText);
469 public boolean isSelected(AnActionEvent e) {
470 return myWatchesReturnValues;
474 public void setSelected(AnActionEvent e, boolean watch) {
475 myWatchesReturnValues = watch;
476 PyDebuggerSettings.getInstance().setWatchReturnValues(watch);
477 final Project project = e.getProject();
478 if (project != null) {
479 myProcess.setShowReturnValues(myWatchesReturnValues);
480 myProcess.getSession().rebuildViews();
485 private static class SimplifiedView extends ToggleAction {
486 private volatile boolean mySimplifiedView;
487 private final PyDebugProcess myProcess;
488 private final String myText;
490 public SimplifiedView(@NotNull PyDebugProcess debugProcess) {
491 super("", "Disables watching classes, functions and modules objects", null);
492 mySimplifiedView = PyDebuggerSettings.getInstance().isSimplifiedView();
493 myProcess = debugProcess;
494 myText = "Simplified Variables View";
498 public void update(@NotNull final AnActionEvent e) {
500 final Presentation presentation = e.getPresentation();
501 presentation.setEnabled(true);
502 presentation.setText(myText);
506 public boolean isSelected(AnActionEvent e) {
507 return mySimplifiedView;
511 public void setSelected(AnActionEvent e, boolean hide) {
512 mySimplifiedView = hide;
513 PyDebuggerSettings.getInstance().setSimplifiedView(hide);
514 myProcess.getSession().rebuildViews();
518 public void setShowReturnValues(boolean showReturnValues) {
519 myDebugger.setShowReturnValues(showReturnValues);
523 public void startStepOver(@Nullable XSuspendContext context) {
524 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OVER);
528 public void startStepInto(@Nullable XSuspendContext context) {
529 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO);
532 public void startStepIntoMyCode(@Nullable XSuspendContext context) {
533 if (!checkCanPerformCommands()) return;
534 getSession().sessionResumed();
535 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
539 public void startStepOut(@Nullable XSuspendContext context) {
540 passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OUT);
543 public void startSmartStepInto(String functionName) {
546 for (PyThreadInfo suspendedThread : mySuspendedThreads) {
547 myDebugger.smartStepInto(suspendedThread.getId(), functionName);
558 public void resume(@Nullable XSuspendContext context) {
559 passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
563 public void startPausing() {
565 myDebugger.suspendAllThreads();
569 public void suspendAllOtherThreads(PyThreadInfo thread) {
570 myDebugger.suspendOtherThreads(thread);
574 * Check if there is the thread suspended on the breakpoint with "Suspend all" policy
576 * @return true if this thread exists
579 public boolean isSuspendedOnAllThreadsPolicy() {
580 if (getSession().isSuspended()) {
581 for (PyThreadInfo threadInfo : getThreads()) {
582 final List<PyStackFrameInfo> frames = threadInfo.getFrames();
583 if ((threadInfo.getState() == PyThreadInfo.State.SUSPENDED) && (frames != null)) {
584 XBreakpoint<?> breakpoint = null;
585 if (threadInfo.isStopOnBreakpoint()) {
586 final PySourcePosition position = frames.get(0).getPosition();
587 breakpoint = myRegisteredBreakpoints.get(position);
589 else if (threadInfo.isExceptionBreak()) {
590 String exceptionName = threadInfo.getMessage();
591 if (exceptionName != null) {
592 breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
595 if ((breakpoint != null) && (breakpoint.getType().isSuspendThreadSupported()) &&
596 (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
605 private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
608 for (PyThreadInfo thread : myDebugger.getThreads()) {
609 myDebugger.resumeOrStep(thread.getId(), mode);
614 private void passToCurrentThread(@Nullable XSuspendContext context, final ResumeOrStepCommand.Mode mode) {
617 String threadId = threadIdBeforeResumeOrStep(context);
619 for (PyThreadInfo suspendedThread : mySuspendedThreads) {
620 if (threadId == null || threadId.equals(suspendedThread.getId())) {
621 myDebugger.resumeOrStep(suspendedThread.getId(), mode);
629 private static String threadIdBeforeResumeOrStep(@Nullable XSuspendContext context) {
630 if (context instanceof PySuspendContext) {
631 return ((PySuspendContext)context).getActiveExecutionStack().getThreadId();
638 protected boolean isConnected() {
639 return myDebugger.isConnected();
642 protected void disconnect() {
643 myDebugger.disconnect();
647 public boolean isDownloadSources() {
648 return myDownloadSources;
651 public void setDownloadSources(boolean downloadSources) {
652 myDownloadSources = downloadSources;
655 protected void cleanUp() {
656 mySuspendedThreads.clear();
657 myDownloadSources = false;
661 public void runToPosition(@NotNull final XSourcePosition position, @Nullable XSuspendContext context) {
663 if (isConnected() && !mySuspendedThreads.isEmpty()) {
664 final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
665 String type = PyLineBreakpointType.ID;
666 AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
668 final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
669 if (document != null) {
670 for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
671 if (breakpointType instanceof PyBreakpointType &&
672 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
673 type = breakpointType.getId();
682 myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
684 passToCurrentThread(context, ResumeOrStepCommand.Mode.RESUME);
689 public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
691 final PyStackFrame frame = currentFrame();
692 return evaluate(expression, execute, frame, doTrunc);
695 private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
696 return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
699 public void consoleExec(String command, PyDebugCallback<String> callback) {
702 final PyStackFrame frame = currentFrame();
703 myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
705 catch (PyDebuggerException e) {
712 public XValueChildrenList loadFrame() throws PyDebuggerException {
713 final PyStackFrame frame = currentFrame();
714 //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
715 if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
716 XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
717 myStackFrameCache.put(frame.getThreadFrameId(), values);
719 return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
722 private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
723 if (myNewVariableValue.containsKey(threadFrameId)) {
724 PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
725 XValueChildrenList res = new XValueChildrenList();
726 for (int i = 0; i < pyDebugValues.size(); i++) {
727 final String name = pyDebugValues.getName(i);
728 if (name.equals(newValue.getName())) {
729 res.add(name, newValue);
732 res.add(name, pyDebugValues.getValue(i));
738 return pyDebugValues;
743 public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
744 final PyStackFrame frame = currentFrame();
745 PyDebugValue debugValue = var.setName(var.getFullName());
746 return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), debugValue);
750 public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
752 final PyStackFrame frame = currentFrame();
753 myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
755 catch (PyDebuggerException e) {
761 public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
762 final PyStackFrame frame = currentFrame();
763 PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
764 myNewVariableValue.put(frame.getThreadFrameId(), newValue);
769 public PyReferrersLoader getReferrersLoader() {
770 if (myReferrersProvider == null) {
771 myReferrersProvider = new PyReferrersLoader(this);
773 return myReferrersProvider;
777 public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
778 throws PyDebuggerException {
779 final PyStackFrame frame = currentFrame();
780 return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
784 public String loadSource(String path) {
785 return myDebugger.loadSource(path);
789 public boolean canSaveToTemp(String name) {
790 final Project project = getSession().getProject();
791 return PyDebugSupportUtils.canSaveToTemp(project, name);
795 private PyStackFrame currentFrame() throws PyDebuggerException {
796 if (!isConnected()) {
797 throw new PyDebuggerException("Disconnected");
800 final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
802 if (frame == null && myConsoleContextFrame != null) {
803 return myConsoleContextFrame;
807 throw new PyDebuggerException("Process is running");
814 private String getFunctionName(final XLineBreakpoint breakpoint) {
815 if (breakpoint.getSourcePosition() == null) {
818 final VirtualFile file = breakpoint.getSourcePosition().getFile();
819 AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
821 final Document document = FileDocumentManager.getInstance().getDocument(file);
822 final Project project = getSession().getProject();
823 if (document != null) {
824 if (file.getFileType() == PythonFileType.INSTANCE) {
825 PsiElement psiElement = XDebuggerUtil.getInstance().
826 findContextElement(file, document.getLineStartOffset(breakpoint.getSourcePosition().getLine()), project, false);
827 PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
828 if (function != null) {
829 return function.getName();
840 public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
841 myRegisteredBreakpoints.put(position, breakpoint);
843 final String conditionExpression = breakpoint.getConditionExpression() == null
845 : breakpoint.getConditionExpression().getExpression();
846 final String logExpression = breakpoint.getLogExpressionObject() == null
848 : breakpoint.getLogExpressionObject().getExpression();
849 SuspendPolicy policy = breakpoint.getType().isSuspendThreadSupported()? breakpoint.getSuspendPolicy(): SuspendPolicy.NONE;
850 myDebugger.setBreakpoint(breakpoint.getType().getId(),
855 getFunctionName(breakpoint),
861 public void addTemporaryBreakpoint(String typeId, String file, int line) {
863 myDebugger.setTempBreakpoint(typeId, file, line);
867 public void removeBreakpoint(final PySourcePosition position) {
868 XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
869 if (breakpoint != null) {
870 myRegisteredBreakpoints.remove(position);
872 myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
877 public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
878 myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
880 myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
884 public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
885 myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
887 myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
891 public Collection<PyThreadInfo> getThreads() {
892 return myDebugger.getThreads();
896 public void threadSuspended(final PyThreadInfo threadInfo, boolean updateSourcePosition) {
897 if (!mySuspendedThreads.contains(threadInfo)) {
898 mySuspendedThreads.add(threadInfo);
900 final List<PyStackFrameInfo> frames = threadInfo.getFrames();
901 if (frames != null) {
902 final PySuspendContext suspendContext = createSuspendContext(threadInfo);
904 XBreakpoint<?> breakpoint = null;
905 if (threadInfo.isStopOnBreakpoint()) {
906 final PySourcePosition position = frames.get(0).getPosition();
907 breakpoint = myRegisteredBreakpoints.get(position);
908 if (breakpoint == null) {
909 myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
912 else if (threadInfo.isExceptionBreak()) {
913 String exceptionName = threadInfo.getMessage();
914 threadInfo.setMessage(null);
915 if (exceptionName != null) {
916 breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
919 if (breakpoint != null) {
920 if ((breakpoint.getType().isSuspendThreadSupported()) && (breakpoint.getSuspendPolicy() == SuspendPolicy.ALL)) {
921 suspendAllOtherThreads(threadInfo);
925 if (updateSourcePosition) {
926 if (breakpoint != null) {
927 if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
928 resume(suspendContext);
932 getSession().positionReached(suspendContext);
940 protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
941 return new PySuspendContext(this, threadInfo);
945 public void threadResumed(final PyThreadInfo threadInfo) {
946 mySuspendedThreads.remove(threadInfo);
949 private void dropFrameCaches() {
950 myStackFrameCache.clear();
951 myNewVariableValue.clear();
955 public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
958 final PyStackFrame frame = currentFrame();
959 return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
961 return Lists.newArrayList();
965 public String getDescription(String prefix) throws Exception {
968 final PyStackFrame frame = currentFrame();
969 return myDebugger.getDescription(frame.getThreadId(), frame.getFrameId(), prefix);
976 public void startNotified(ProcessEvent event) {
980 public void processTerminated(ProcessEvent event) {
985 public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
990 public void onTextAvailable(ProcessEvent event, Key outputType) {
993 public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
994 return new PyStackFrame(getSession().getProject(), this, frameInfo,
995 getPositionConverter().convertFromPython(frameInfo.getPosition()));
999 public String getCurrentStateMessage() {
1000 if (getSession().isStopped()) {
1001 return XDebuggerBundle.message("debugger.state.message.disconnected");
1003 else if (isConnected()) {
1004 return XDebuggerBundle.message("debugger.state.message.connected");
1007 return getConnectionMessage();
1011 public void addProcessListener(ProcessListener listener) {
1012 ProcessHandler handler = doGetProcessHandler();
1013 if (handler != null) {
1014 handler.addProcessListener(listener);
1018 public boolean isWaitingForConnection() {
1019 return myWaitingForConnection;
1022 public void setWaitingForConnection(boolean waitingForConnection) {
1023 myWaitingForConnection = waitingForConnection;
1026 public int getConnectTimeout() {
1027 return CONNECTION_TIMEOUT;
1032 private XSourcePosition getCurrentFrameSourcePosition() {
1034 PyStackFrame frame = currentFrame();
1036 return frame.getSourcePosition();
1038 catch (PyDebuggerException e) {
1043 public Project getProject() {
1044 return getSession().getProject();
1049 public XSourcePosition getSourcePositionForName(String name, String parentType) {
1050 if (name == null) return null;
1051 XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1053 final PsiFile file = getPsiFile(currentPosition);
1055 if (file == null) return null;
1057 if (Strings.isNullOrEmpty(parentType)) {
1058 final Ref<PsiElement> elementRef = resolveInCurrentFrame(name, currentPosition, file);
1059 return elementRef.isNull() ? null : XDebuggerUtil.getInstance().createPositionByElement(elementRef.get());
1062 final PyType parentDef = resolveTypeFromString(parentType, file);
1063 if (parentDef == null) {
1066 List<? extends RatedResolveResult> results =
1067 parentDef.resolveMember(name, null, AccessDirection.READ, PyResolveContext.noImplicits());
1068 if (results != null && !results.isEmpty()) {
1069 return XDebuggerUtil.getInstance().createPositionByElement(results.get(0).getElement());
1072 return typeToPosition(parentDef); // at least try to return parent
1079 private static Ref<PsiElement> resolveInCurrentFrame(final String name, XSourcePosition currentPosition, PsiFile file) {
1080 final Ref<PsiElement> elementRef = Ref.create();
1081 PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
1083 if (currentElement == null) {
1088 PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
1090 public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
1091 if ((element instanceof PyImportElement)) {
1092 PyImportElement importElement = (PyImportElement)element;
1093 if (name.equals(importElement.getVisibleName())) {
1094 if (elementRef.isNull()) {
1095 elementRef.set(element);
1102 if (elementRef.isNull()) {
1103 elementRef.set(element);
1111 public <T> T getHint(@NotNull Key<T> hintKey) {
1116 public void handleEvent(@NotNull Event event, @Nullable Object associated) {
1119 }, currentElement, name, null);
1124 private PsiFile getPsiFile(XSourcePosition currentPosition) {
1125 if (currentPosition == null) {
1129 return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
1135 public XSourcePosition getSourcePositionForType(String typeName) {
1136 XSourcePosition currentPosition = getCurrentFrameSourcePosition();
1138 final PsiFile file = getPsiFile(currentPosition);
1140 if (file == null || typeName == null || !(file instanceof PyFile)) return null;
1143 final PyType pyType = resolveTypeFromString(typeName, file);
1144 return pyType == null ? null : typeToPosition(pyType);
1148 public void showNumericContainer(PyDebugValue value) {
1149 PyViewNumericContainerAction.showNumericViewer(getProject(), value);
1153 private static XSourcePosition typeToPosition(PyType pyType) {
1154 final PyClassType classType = PyUtil.as(pyType, PyClassType.class);
1156 if (classType != null) {
1157 return XDebuggerUtil.getInstance().createPositionByElement(classType.getPyClass());
1160 final PyModuleType moduleType = PyUtil.as(pyType, PyModuleType.class);
1161 if (moduleType != null) {
1162 return XDebuggerUtil.getInstance().createPositionByElement(moduleType.getModule());
1167 private PyType resolveTypeFromString(String typeName, PsiFile file) {
1168 typeName = typeName.replace("__builtin__.", "");
1169 PyType pyType = null;
1170 if (!typeName.contains(".")) {
1172 pyType = PyTypeParser.getTypeByName(file, typeName);
1174 if (pyType == null) {
1175 PyElementGenerator generator = PyElementGenerator.getInstance(getProject());
1176 PyPsiFacade psiFacade = PyPsiFacade.getInstance(getProject());
1177 PsiFile dummyFile = generator.createDummyFile(((PyFile)file).getLanguageLevel(), "");
1178 Module moduleForFile = ModuleUtilCore.findModuleForPsiElement(file);
1179 dummyFile.putUserData(ModuleUtilCore.KEY_MODULE, moduleForFile);
1181 pyType = psiFacade.parseTypeAnnotation(typeName, dummyFile);
1186 private interface DebuggerFactory {
1187 @NotNull ProcessDebugger createDebugger(@NotNull PyDebugProcess process);