--Properly process objects which are type definitions
[idea/community.git] / python / src / com / jetbrains / python / debugger / PyDebugProcess.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.jetbrains.python.debugger;
17
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import com.intellij.execution.process.ProcessEvent;
21 import com.intellij.execution.process.ProcessHandler;
22 import com.intellij.execution.process.ProcessListener;
23 import com.intellij.execution.ui.ConsoleView;
24 import com.intellij.execution.ui.ConsoleViewContentType;
25 import com.intellij.execution.ui.ExecutionConsole;
26 import com.intellij.openapi.application.AccessToken;
27 import com.intellij.openapi.application.ApplicationInfo;
28 import com.intellij.openapi.application.ApplicationManager;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.editor.Document;
31 import com.intellij.openapi.extensions.Extensions;
32 import com.intellij.openapi.fileEditor.FileDocumentManager;
33 import com.intellij.openapi.progress.ProgressIndicator;
34 import com.intellij.openapi.progress.ProgressManager;
35 import com.intellij.openapi.progress.Task;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.ui.Messages;
38 import com.intellij.openapi.util.Key;
39 import com.intellij.openapi.util.Ref;
40 import com.intellij.openapi.vfs.VirtualFile;
41 import com.intellij.psi.PsiElement;
42 import com.intellij.psi.PsiFile;
43 import com.intellij.psi.PsiManager;
44 import com.intellij.psi.ResolveState;
45 import com.intellij.psi.scope.PsiScopeProcessor;
46 import com.intellij.psi.util.PsiTreeUtil;
47 import com.intellij.remote.RemoteProcessControl;
48 import com.intellij.util.ui.UIUtil;
49 import com.intellij.xdebugger.*;
50 import com.intellij.xdebugger.breakpoints.XBreakpoint;
51 import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
52 import com.intellij.xdebugger.breakpoints.XBreakpointType;
53 import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
54 import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
55 import com.intellij.xdebugger.frame.XSuspendContext;
56 import com.intellij.xdebugger.frame.XValueChildrenList;
57 import com.intellij.xdebugger.impl.XSourcePositionImpl;
58 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
59 import com.jetbrains.python.PythonFileType;
60 import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
61 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
62 import com.jetbrains.python.debugger.pydev.*;
63 import com.jetbrains.python.psi.*;
64 import com.jetbrains.python.psi.resolve.PyResolveUtil;
65 import com.jetbrains.python.psi.types.PyClassType;
66 import com.jetbrains.python.psi.types.PyModuleType;
67 import com.jetbrains.python.psi.types.PyType;
68 import com.jetbrains.python.psi.types.PyTypeParser;
69 import org.jetbrains.annotations.NotNull;
70 import org.jetbrains.annotations.Nullable;
71
72 import java.io.IOException;
73 import java.net.ServerSocket;
74 import java.util.*;
75 import java.util.concurrent.ConcurrentHashMap;
76
77 import static javax.swing.SwingUtilities.invokeLater;
78
79 /**
80  * @author yole
81  */
82 // todo: bundle messages
83 // todo: pydevd supports module reloading - look for a way to use the feature
84 public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, ProcessListener {
85
86   private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.PyDebugProcess");
87   private static final int CONNECTION_TIMEOUT = 60000;
88
89   private final ProcessDebugger myDebugger;
90   private final XBreakpointHandler[] myBreakpointHandlers;
91   private final PyDebuggerEditorsProvider myEditorsProvider;
92   private final ProcessHandler myProcessHandler;
93   private final ExecutionConsole myExecutionConsole;
94   private final Map<PySourcePosition, XLineBreakpoint> myRegisteredBreakpoints = new ConcurrentHashMap<PySourcePosition, XLineBreakpoint>();
95   private final Map<String, XBreakpoint<? extends ExceptionBreakpointProperties>> myRegisteredExceptionBreakpoints =
96     new ConcurrentHashMap<String, XBreakpoint<? extends ExceptionBreakpointProperties>>();
97
98   private final List<PyThreadInfo> mySuspendedThreads = Collections.synchronizedList(Lists.<PyThreadInfo>newArrayList());
99   private final Map<String, XValueChildrenList> myStackFrameCache = Maps.newHashMap();
100   private final Map<String, PyDebugValue> myNewVariableValue = Maps.newHashMap();
101   private boolean myDownloadSources = false;
102
103   private boolean myClosing = false;
104
105   private PyPositionConverter myPositionConverter;
106   private final XSmartStepIntoHandler<?> mySmartStepIntoHandler;
107   private boolean myWaitingForConnection = false;
108   private PyStackFrame myConsoleContextFrame = null;
109   private PyReferrersLoader myReferrersProvider;
110
111   public PyDebugProcess(final @NotNull XDebugSession session,
112                         @NotNull final ServerSocket serverSocket,
113                         @NotNull final ExecutionConsole executionConsole,
114                         @Nullable final ProcessHandler processHandler, boolean multiProcess) {
115     super(session);
116     session.setPauseActionSupported(true);
117     if (multiProcess) {
118       myDebugger = createMultiprocessDebugger(serverSocket);
119     }
120     else {
121       myDebugger = new RemoteDebugger(this, serverSocket, getConnectTimeout());
122     }
123
124     List<XBreakpointHandler> breakpointHandlers = new ArrayList<XBreakpointHandler>();
125     breakpointHandlers.add(new PyLineBreakpointHandler(this));
126     breakpointHandlers.add(new PyExceptionBreakpointHandler(this));
127     for (PyBreakpointHandlerFactory factory : Extensions.getExtensions(PyBreakpointHandlerFactory.EP_NAME)) {
128       breakpointHandlers.add(factory.createBreakpointHandler(this));
129     }
130     myBreakpointHandlers = breakpointHandlers.toArray(new XBreakpointHandler[breakpointHandlers.size()]);
131
132     myEditorsProvider = new PyDebuggerEditorsProvider();
133     mySmartStepIntoHandler = new PySmartStepIntoHandler(this);
134     myProcessHandler = processHandler;
135     myExecutionConsole = executionConsole;
136     if (myProcessHandler != null) {
137       myProcessHandler.addProcessListener(this);
138     }
139     if (processHandler instanceof PositionConverterProvider) {
140       myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
141     }
142     else {
143       myPositionConverter = new PyLocalPositionConverter();
144     }
145     myDebugger.addCloseListener(new RemoteDebuggerCloseListener() {
146       @Override
147       public void closed() {
148         handleStop();
149       }
150
151       @Override
152       public void communicationError() {
153         detachDebuggedProcess();
154       }
155
156       @Override
157       public void detached() {
158         detachDebuggedProcess();
159       }
160     });
161   }
162
163   private MultiProcessDebugger createMultiprocessDebugger(ServerSocket serverSocket) {
164     MultiProcessDebugger debugger = new MultiProcessDebugger(this, serverSocket, 10000);
165     debugger.addOtherDebuggerCloseListener(new MultiProcessDebugger.DebuggerProcessListener() {
166       @Override
167       public void threadsClosed(Set<String> threadIds) {
168         for (PyThreadInfo t : mySuspendedThreads) {
169           if (threadIds.contains(t.getId())) {
170             if (getSession().isSuspended()) {
171               getSession().resume();
172               break;
173             }
174           }
175         }
176       }
177     });
178     return debugger;
179   }
180
181   protected void detachDebuggedProcess() {
182     handleStop(); //in case of normal debug we stop the session
183   }
184
185   protected void handleStop() {
186     getSession().stop();
187   }
188
189   public void setPositionConverter(PyPositionConverter positionConverter) {
190     myPositionConverter = positionConverter;
191   }
192
193
194   @Override
195   public PyPositionConverter getPositionConverter() {
196     return myPositionConverter;
197   }
198
199   @NotNull
200   @Override
201   public XBreakpointHandler<?>[] getBreakpointHandlers() {
202     return myBreakpointHandlers;
203   }
204
205   @Override
206   @NotNull
207   public XDebuggerEditorsProvider getEditorsProvider() {
208     return myEditorsProvider;
209   }
210
211   @Override
212   @Nullable
213   protected ProcessHandler doGetProcessHandler() {
214     return myProcessHandler;
215   }
216
217   @Override
218   @NotNull
219   public ExecutionConsole createConsole() {
220     return myExecutionConsole;
221   }
222
223   @Override
224   public XSmartStepIntoHandler<?> getSmartStepIntoHandler() {
225     return mySmartStepIntoHandler;
226   }
227
228   @Override
229   public void sessionInitialized() {
230     waitForConnection(getConnectionMessage(), getConnectionTitle());
231   }
232
233   protected void waitForConnection(final String connectionMessage, String connectionTitle) {
234     ProgressManager.getInstance().run(new Task.Backgroundable(getSession().getProject(), connectionTitle, false) {
235       @Override
236       public void run(@NotNull final ProgressIndicator indicator) {
237         indicator.setText(connectionMessage);
238         try {
239           beforeConnect();
240           myWaitingForConnection = true;
241           myDebugger.waitForConnect();
242           myWaitingForConnection = false;
243           afterConnect();
244
245           handshake();
246           init();
247           myDebugger.run();
248         }
249         catch (final Exception e) {
250           myWaitingForConnection = false;
251           if (myProcessHandler != null) {
252             myProcessHandler.destroyProcess();
253           }
254           if (!myClosing) {
255             invokeLater(new Runnable() {
256               @Override
257               public void run() {
258                 Messages.showErrorDialog("Unable to establish connection with debugger:\n" + e.getMessage(), getConnectionTitle());
259               }
260             });
261           }
262         }
263       }
264     });
265   }
266
267   @Override
268   public void init() {
269     getSession().rebuildViews();
270     registerBreakpoints();
271   }
272
273   @Override
274   public int handleDebugPort(int localPort) throws IOException {
275     if (myProcessHandler instanceof RemoteProcessControl) {
276       return getRemoteTunneledPort(localPort, (RemoteProcessControl)myProcessHandler);
277     }
278     else {
279       return localPort;
280     }
281   }
282
283   protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessControl handler) throws IOException {
284     try {
285       return handler.getRemoteSocket(localPort).getSecond();
286     }
287     catch (Exception e) {
288       throw new IOException(e);
289     }
290   }
291
292   @Override
293   public void recordSignature(PySignature signature) {
294     PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
295   }
296
297   @Override
298   public void recordLogEvent(PyConcurrencyEvent event) {
299     PyConcurrencyService.getInstance(getSession().getProject()).recordEvent(getSession(), event, event.isAsyncio());
300   }
301
302   @Override
303   public void showConsole(PyThreadInfo thread) {
304     myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame();
305     if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) {
306       UIUtil.invokeLaterIfNeeded(new Runnable() {
307         @Override
308         public void run() {
309           ((PythonDebugLanguageConsoleView)myExecutionConsole).enableConsole(false);
310         }
311       });
312     }
313   }
314
315   protected void afterConnect() {
316   }
317
318   protected void beforeConnect() {
319   }
320
321   protected String getConnectionMessage() {
322     return "Waiting for connection...";
323   }
324
325   protected String getConnectionTitle() {
326     return "Connecting To Debugger";
327   }
328
329   private void handshake() throws PyDebuggerException {
330     String remoteVersion = myDebugger.handshake();
331     String currentBuild = ApplicationInfo.getInstance().getBuild().asStringWithoutProductCode();
332     if ("@@BUILD_NUMBER@@".equals(remoteVersion)) {
333       remoteVersion = currentBuild;
334     }
335     else if (remoteVersion.startsWith("PY-")) {
336       remoteVersion = remoteVersion.substring(3);
337     }
338     else {
339       remoteVersion = null;
340     }
341     printToConsole("Connected to pydev debugger (build " + remoteVersion + ")\n", ConsoleViewContentType.SYSTEM_OUTPUT);
342
343     if (remoteVersion != null) {
344       if (!(remoteVersion.equals(currentBuild) || remoteVersion.startsWith(currentBuild))) {
345         LOG.warn(String.format("Wrong debugger version. Remote version: %s Current build: %s", remoteVersion, currentBuild));
346         printToConsole("Warning: wrong debugger version. Use pycharm-debugger.egg from PyCharm installation folder.\n",
347                        ConsoleViewContentType.ERROR_OUTPUT);
348       }
349     }
350   }
351
352   @Override
353   public void printToConsole(String text, ConsoleViewContentType contentType) {
354     ((ConsoleView)myExecutionConsole).print(text, contentType);
355   }
356
357   private void registerBreakpoints() {
358     registerLineBreakpoints();
359     registerExceptionBreakpoints();
360   }
361
362   private void registerExceptionBreakpoints() {
363     for (XBreakpoint<? extends ExceptionBreakpointProperties> bp : myRegisteredExceptionBreakpoints.values()) {
364       addExceptionBreakpoint(bp);
365     }
366   }
367
368   public void registerLineBreakpoints() {
369     for (Map.Entry<PySourcePosition, XLineBreakpoint> entry : myRegisteredBreakpoints.entrySet()) {
370       addBreakpoint(entry.getKey(), entry.getValue());
371     }
372   }
373
374   @Override
375   public void startStepOver(@Nullable XSuspendContext context) {
376     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OVER);
377   }
378
379   @Override
380   public void startStepInto(@Nullable XSuspendContext context) {
381     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO);
382   }
383
384   public void startStepIntoMyCode(@Nullable XSuspendContext context) {
385     if (!checkCanPerformCommands()) return;
386     getSession().sessionResumed();
387     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_INTO_MY_CODE);
388   }
389
390   @Override
391   public void startStepOut(@Nullable XSuspendContext context) {
392     passToCurrentThread(context, ResumeOrStepCommand.Mode.STEP_OUT);
393   }
394
395   public void startSmartStepInto(String functionName) {
396     dropFrameCaches();
397     if (isConnected()) {
398       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
399         myDebugger.smartStepInto(suspendedThread.getId(), functionName);
400       }
401     }
402   }
403
404   @Override
405   public void stop() {
406     myDebugger.close();
407   }
408
409   @Override
410   public void resume(@Nullable XSuspendContext context) {
411     passToAllThreads(ResumeOrStepCommand.Mode.RESUME);
412   }
413
414   @Override
415   public void startPausing() {
416     if (isConnected()) {
417       myDebugger.suspendAllThreads();
418     }
419   }
420
421   private void passToAllThreads(final ResumeOrStepCommand.Mode mode) {
422     dropFrameCaches();
423     if (isConnected()) {
424       for (PyThreadInfo suspendedThread : Lists.newArrayList(mySuspendedThreads)) {
425         myDebugger.resumeOrStep(suspendedThread.getId(), mode);
426       }
427     }
428   }
429
430   private void passToCurrentThread(@Nullable XSuspendContext context, final ResumeOrStepCommand.Mode mode) {
431     dropFrameCaches();
432     if (isConnected()) {
433       String threadId = threadIdBeforeResumeOrStep(context);
434
435       for (PyThreadInfo suspendedThread : mySuspendedThreads) {
436         if (threadId == null || threadId.equals(suspendedThread.getId())) {
437           myDebugger.resumeOrStep(suspendedThread.getId(), mode);
438           break;
439         }
440       }
441     }
442   }
443
444   @Nullable
445   private static String threadIdBeforeResumeOrStep(@Nullable XSuspendContext context) {
446     if (context instanceof PySuspendContext) {
447       return ((PySuspendContext)context).getActiveExecutionStack().getThreadId();
448     }
449     else {
450       return null;
451     }
452   }
453
454   protected boolean isConnected() {
455     return myDebugger.isConnected();
456   }
457
458   protected void disconnect() {
459     myDebugger.disconnect();
460     cleanUp();
461   }
462
463   public boolean isDownloadSources() {
464     return myDownloadSources;
465   }
466
467   public void setDownloadSources(boolean downloadSources) {
468     myDownloadSources = downloadSources;
469   }
470
471   protected void cleanUp() {
472     mySuspendedThreads.clear();
473     myDownloadSources = false;
474   }
475
476   @Override
477   public void runToPosition(@NotNull final XSourcePosition position, @Nullable XSuspendContext context) {
478     dropFrameCaches();
479     if (isConnected() && !mySuspendedThreads.isEmpty()) {
480       final PySourcePosition pyPosition = myPositionConverter.convertToPython(position);
481       String type = PyLineBreakpointType.ID;
482       AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
483       try {
484         final Document document = FileDocumentManager.getInstance().getDocument(position.getFile());
485         if (document != null) {
486           for (XBreakpointType breakpointType : Extensions.getExtensions(XBreakpointType.EXTENSION_POINT_NAME)) {
487             if (breakpointType instanceof PyBreakpointType &&
488                 ((PyBreakpointType)breakpointType).canPutInDocument(getSession().getProject(), document)) {
489               type = breakpointType.getId();
490               break;
491             }
492           }
493         }
494       }
495       finally {
496         lock.finish();
497       }
498       myDebugger.setTempBreakpoint(type, pyPosition.getFile(), pyPosition.getLine());
499
500       passToCurrentThread(context, ResumeOrStepCommand.Mode.RESUME);
501     }
502   }
503
504   @Override
505   public PyDebugValue evaluate(final String expression, final boolean execute, boolean doTrunc) throws PyDebuggerException {
506     dropFrameCaches();
507     final PyStackFrame frame = currentFrame();
508     return evaluate(expression, execute, frame, doTrunc);
509   }
510
511   private PyDebugValue evaluate(String expression, boolean execute, PyStackFrame frame, boolean trimResult) throws PyDebuggerException {
512     return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
513   }
514
515   public void consoleExec(String command, PyDebugCallback<String> callback) {
516     dropFrameCaches();
517     try {
518       final PyStackFrame frame = currentFrame();
519       myDebugger.consoleExec(frame.getThreadId(), frame.getFrameId(), command, callback);
520     }
521     catch (PyDebuggerException e) {
522       callback.error(e);
523     }
524   }
525
526   @Override
527   @Nullable
528   public XValueChildrenList loadFrame() throws PyDebuggerException {
529     final PyStackFrame frame = currentFrame();
530     //do not reload frame every time it is needed, because due to bug in pdb, reloading frame clears all variable changes
531     if (!myStackFrameCache.containsKey(frame.getThreadFrameId())) {
532       XValueChildrenList values = myDebugger.loadFrame(frame.getThreadId(), frame.getFrameId());
533       myStackFrameCache.put(frame.getThreadFrameId(), values);
534     }
535     return applyNewValue(myStackFrameCache.get(frame.getThreadFrameId()), frame.getThreadFrameId());
536   }
537
538   private XValueChildrenList applyNewValue(XValueChildrenList pyDebugValues, String threadFrameId) {
539     if (myNewVariableValue.containsKey(threadFrameId)) {
540       PyDebugValue newValue = myNewVariableValue.get(threadFrameId);
541       XValueChildrenList res = new XValueChildrenList();
542       for (int i = 0; i < pyDebugValues.size(); i++) {
543         final String name = pyDebugValues.getName(i);
544         if (name.equals(newValue.getName())) {
545           res.add(name, newValue);
546         }
547         else {
548           res.add(name, pyDebugValues.getValue(i));
549         }
550       }
551       return res;
552     }
553     else {
554       return pyDebugValues;
555     }
556   }
557
558   @Override
559   public XValueChildrenList loadVariable(final PyDebugValue var) throws PyDebuggerException {
560     final PyStackFrame frame = currentFrame();
561     return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), var);
562   }
563
564   @Override
565   public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
566     try {
567       final PyStackFrame frame = currentFrame();
568       myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
569     }
570     catch (PyDebuggerException e) {
571       callback.error(e);
572     }
573   }
574
575   @Override
576   public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
577     final PyStackFrame frame = currentFrame();
578     PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
579     myNewVariableValue.put(frame.getThreadFrameId(), newValue);
580   }
581
582   @Nullable
583   @Override
584   public PyReferrersLoader getReferrersLoader() {
585     if (myReferrersProvider == null) {
586       myReferrersProvider = new PyReferrersLoader(this);
587     }
588     return myReferrersProvider;
589   }
590
591   @Override
592   public ArrayChunk getArrayItems(PyDebugValue var, int rowOffset, int colOffset, int rows, int cols, String format)
593     throws PyDebuggerException {
594     final PyStackFrame frame = currentFrame();
595     return myDebugger.loadArrayItems(frame.getThreadId(), frame.getFrameId(), var, rowOffset, colOffset, rows, cols, format);
596   }
597
598   @Nullable
599   public String loadSource(String path) {
600     return myDebugger.loadSource(path);
601   }
602
603   @Override
604   public boolean canSaveToTemp(String name) {
605     final Project project = getSession().getProject();
606     return PyDebugSupportUtils.canSaveToTemp(project, name);
607   }
608
609   @NotNull
610   private PyStackFrame currentFrame() throws PyDebuggerException {
611     if (!isConnected()) {
612       throw new PyDebuggerException("Disconnected");
613     }
614
615     final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame();
616
617     if (frame == null && myConsoleContextFrame != null) {
618       return myConsoleContextFrame;
619     }
620
621     if (frame == null) {
622       throw new PyDebuggerException("Process is running");
623     }
624
625     return frame;
626   }
627
628   @Nullable
629   private String getFunctionName(final XLineBreakpoint breakpoint) {
630     if (breakpoint.getSourcePosition() == null) {
631       return null;
632     }
633     final VirtualFile file = breakpoint.getSourcePosition().getFile();
634     AccessToken lock = ApplicationManager.getApplication().acquireReadActionLock();
635     try {
636       final Document document = FileDocumentManager.getInstance().getDocument(file);
637       final Project project = getSession().getProject();
638       if (document != null) {
639         if (file.getFileType() == PythonFileType.INSTANCE) {
640           PsiElement psiElement = XDebuggerUtil.getInstance().findContextElement(file, breakpoint.getSourcePosition().getOffset(),
641                                                                                  project, false);
642           PyFunction function = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
643           if (function != null) {
644             return function.getName();
645           }
646         }
647       }
648       return null;
649     }
650     finally {
651       lock.finish();
652     }
653   }
654
655   public void addBreakpoint(final PySourcePosition position, final XLineBreakpoint breakpoint) {
656     myRegisteredBreakpoints.put(position, breakpoint);
657     if (isConnected()) {
658       final String conditionExpression = breakpoint.getConditionExpression() == null
659                                          ? null
660                                          : breakpoint.getConditionExpression().getExpression();
661       final String logExpression = breakpoint.getLogExpressionObject() == null
662                                    ? null
663                                    : breakpoint.getLogExpressionObject().getExpression();
664       myDebugger.setBreakpointWithFuncName(breakpoint.getType().getId(), position.getFile(), position.getLine(),
665                                            conditionExpression,
666                                            logExpression,
667                                            getFunctionName(breakpoint));
668     }
669   }
670
671   public void addTemporaryBreakpoint(String typeId, String file, int line) {
672     if (isConnected()) {
673       myDebugger.setTempBreakpoint(typeId, file, line);
674     }
675   }
676
677   public void removeBreakpoint(final PySourcePosition position) {
678     XLineBreakpoint breakpoint = myRegisteredBreakpoints.get(position);
679     if (breakpoint != null) {
680       myRegisteredBreakpoints.remove(position);
681       if (isConnected()) {
682         myDebugger.removeBreakpoint(breakpoint.getType().getId(), position.getFile(), position.getLine());
683       }
684     }
685   }
686
687   public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
688     myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
689     if (isConnected()) {
690       myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
691     }
692   }
693
694   public void removeExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
695     myRegisteredExceptionBreakpoints.remove(breakpoint.getProperties().getException());
696     if (isConnected()) {
697       myDebugger.removeExceptionBreakpoint(breakpoint.getProperties());
698     }
699   }
700
701   public Collection<PyThreadInfo> getThreads() {
702     return myDebugger.getThreads();
703   }
704
705   @Override
706   public void threadSuspended(final PyThreadInfo threadInfo) {
707     if (!mySuspendedThreads.contains(threadInfo)) {
708       mySuspendedThreads.add(threadInfo);
709
710       final List<PyStackFrameInfo> frames = threadInfo.getFrames();
711       if (frames != null) {
712         final PySuspendContext suspendContext = createSuspendContext(threadInfo);
713
714         XBreakpoint<?> breakpoint = null;
715         if (threadInfo.isStopOnBreakpoint()) {
716           final PySourcePosition position = frames.get(0).getPosition();
717           breakpoint = myRegisteredBreakpoints.get(position);
718           if (breakpoint == null) {
719             myDebugger.removeTempBreakpoint(position.getFile(), position.getLine());
720           }
721         }
722         else if (threadInfo.isExceptionBreak()) {
723           String exceptionName = threadInfo.getMessage();
724           threadInfo.setMessage(null);
725           if (exceptionName != null) {
726             breakpoint = myRegisteredExceptionBreakpoints.get(exceptionName);
727           }
728         }
729
730         if (breakpoint != null) {
731           if (!getSession().breakpointReached(breakpoint, threadInfo.getMessage(), suspendContext)) {
732             resume(suspendContext);
733           }
734         }
735         else {
736           getSession().positionReached(suspendContext);
737         }
738       }
739     }
740   }
741
742   @NotNull
743   protected PySuspendContext createSuspendContext(PyThreadInfo threadInfo) {
744     return new PySuspendContext(this, threadInfo);
745   }
746
747   @Override
748   public void threadResumed(final PyThreadInfo threadInfo) {
749     mySuspendedThreads.remove(threadInfo);
750   }
751
752   private void dropFrameCaches() {
753     myStackFrameCache.clear();
754     myNewVariableValue.clear();
755   }
756
757   @NotNull
758   public List<PydevCompletionVariant> getCompletions(String prefix) throws Exception {
759     if (isConnected()) {
760       dropFrameCaches();
761       final PyStackFrame frame = currentFrame();
762       return myDebugger.getCompletions(frame.getThreadId(), frame.getFrameId(), prefix);
763     }
764     return Lists.newArrayList();
765   }
766
767   @Override
768   public void startNotified(ProcessEvent event) {
769   }
770
771   @Override
772   public void processTerminated(ProcessEvent event) {
773     myDebugger.close();
774   }
775
776   @Override
777   public void processWillTerminate(ProcessEvent event, boolean willBeDestroyed) {
778     myClosing = true;
779   }
780
781   @Override
782   public void onTextAvailable(ProcessEvent event, Key outputType) {
783   }
784
785   public PyStackFrame createStackFrame(PyStackFrameInfo frameInfo) {
786     return new PyStackFrame(getSession().getProject(), this, frameInfo,
787                             getPositionConverter().convertFromPython(frameInfo.getPosition()));
788   }
789
790   @Override
791   public String getCurrentStateMessage() {
792     if (getSession().isStopped()) {
793       return XDebuggerBundle.message("debugger.state.message.disconnected");
794     }
795     else if (isConnected()) {
796       return XDebuggerBundle.message("debugger.state.message.connected");
797     }
798     else {
799       return getConnectionMessage();
800     }
801   }
802
803   public void addProcessListener(ProcessListener listener) {
804     ProcessHandler handler = doGetProcessHandler();
805     if (handler != null) {
806       handler.addProcessListener(listener);
807     }
808   }
809
810   public boolean isWaitingForConnection() {
811     return myWaitingForConnection;
812   }
813
814   public void setWaitingForConnection(boolean waitingForConnection) {
815     myWaitingForConnection = waitingForConnection;
816   }
817
818   public int getConnectTimeout() {
819     return CONNECTION_TIMEOUT;
820   }
821
822
823   @Nullable
824   private XSourcePosition getCurrentFrameSourcePosition() {
825     try {
826       PyStackFrame frame = currentFrame();
827
828       return frame.getSourcePosition();
829     }
830     catch (PyDebuggerException e) {
831       return null;
832     }
833   }
834
835   public Project getProject() {
836     return getSession().getProject();
837   }
838
839   @Nullable
840   @Override
841   public XSourcePosition getSourcePositionForName(String name) {
842     if (name == null)
843       return null;
844     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
845
846     final PsiFile file = getPsiFile(currentPosition);
847
848     if (file == null) return null;
849
850     PsiElement currentElement = file.findElementAt(currentPosition.getOffset());
851
852     if (currentElement == null) {
853       return null;
854     }
855
856     final Ref<PsiElement> elementRef = Ref.create();
857
858     PyResolveUtil.scopeCrawlUp(new PsiScopeProcessor() {
859       @Override
860       public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
861         if ((element instanceof PyImportElement)) {
862           PyImportElement importElement = (PyImportElement)element;
863           if (name.equals(importElement.getVisibleName())) {
864             if (elementRef.isNull()) {
865               elementRef.set(element);
866             }
867             return false;
868
869           }
870           return true;
871         }
872         else {
873           if (elementRef.isNull()) {
874             elementRef.set(element);
875           }
876           return false;
877         }
878       }
879
880       @Nullable
881       @Override
882       public <T> T getHint(@NotNull Key<T> hintKey) {
883         return null;
884       }
885
886       @Override
887       public void handleEvent(@NotNull Event event, @Nullable Object associated) {
888
889       }
890     }, currentElement, name, null);
891
892     return elementRef.isNull() ? null
893                                : XSourcePositionImpl.createByElement(elementRef.get());
894   }
895
896   @Nullable
897   private PsiFile getPsiFile(XSourcePosition currentPosition) {
898     if (currentPosition == null) {
899       return null;
900     }
901
902     return PsiManager.getInstance(getProject()).findFile(currentPosition.getFile());
903   }
904
905
906   @Nullable
907   @Override
908   public XSourcePosition getSourcePositionForType(String typeName) {
909     XSourcePosition currentPosition = getCurrentFrameSourcePosition();
910
911     final PsiFile file = getPsiFile(currentPosition);
912
913     if (file == null || typeName == null || !(file instanceof PyFile)) return null;
914
915
916     typeName = typeName.replace("__builtin__.", "");
917     if (!typeName.contains(".")) {
918       PyType type = PyTypeParser.getTypeByName(file, typeName);
919
920       if (type instanceof PyClassType) {
921         return XSourcePositionImpl.createByElement(((PyClassType)type).getPyClass());
922       }
923     }
924
925     PyElementGenerator generator = PyElementGenerator.getInstance(getProject());
926     PyPsiFacade psiFacade = PyPsiFacade.getInstance(getProject());
927     PyType pyType = psiFacade.parseTypeAnnotation(typeName, generator.createDummyFile(((PyFile)file).getLanguageLevel(), ""));
928
929     if (pyType != null) {
930       final PyClassType classType = PyUtil.as(pyType, PyClassType.class);
931
932       if (classType != null) {
933         return XSourcePositionImpl.createByElement(classType.getPyClass());
934       }
935
936       final PyModuleType moduleType = PyUtil.as(pyType, PyModuleType.class);
937       if (moduleType != null) {
938         return XSourcePositionImpl.createByElement(moduleType.getModule());
939       }
940     }
941
942
943
944
945     return null;
946   }
947 }