Debugger speedups not found notification (PY-24310)

Replace filter in output console with new notification.
This commit is contained in:
Elizaveta Shashkova
2017-06-14 15:45:46 +03:00
parent dfacd78b91
commit 8cec35c1be
13 changed files with 215 additions and 175 deletions
@@ -5,7 +5,6 @@ import com.intellij.execution.filters.InputFilter;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.jetbrains.python.debugger.PyRunCythonExtensionsFilter;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
@@ -21,9 +20,6 @@ public class PyEduConsoleInputFilterProvider implements ConsoleInputFilterProvid
if (outputType.equals(ConsoleViewContentType.SYSTEM_OUTPUT) && !text.contains("exit code")) {
return Collections.emptyList();
}
if (text.startsWith(PyRunCythonExtensionsFilter.WARNING_MESSAGE_BEGIN)) {
return Collections.emptyList();
}
if (text.startsWith("pydev debugger")) {
return Collections.emptyList();
}
@@ -146,6 +146,7 @@ CMD_INPUT_REQUESTED = 147
CMD_GET_DESCRIPTION = 148
CMD_PROCESS_CREATED = 149
CMD_SHOW_CYTHON_WARNING = 150
CMD_VERSION = 501
CMD_RETURN = 502
@@ -204,6 +205,7 @@ ID_TO_MEANING = {
'148': 'CMD_GET_DESCRIPTION',
'149': 'CMD_PROCESS_CREATED',
'150': 'CMD_SHOW_CYTHON_WARNING',
'501': 'CMD_VERSION',
'502': 'CMD_RETURN',
@@ -590,6 +592,12 @@ class NetCommandFactory:
cmdText = '<process/>'
return NetCommand(CMD_PROCESS_CREATED, 0, cmdText)
def make_show_cython_warning_message(self):
try:
return NetCommand(CMD_SHOW_CYTHON_WARNING, 0, '')
except:
return self.make_error_message(0, get_exception_traceback_str())
def make_custom_frame_created_message(self, frameId, frameDescription):
frameDescription = pydevd_xml.make_valid_xml_value(frameDescription)
cmdText = '<xml><thread name="%s" id="%s"/></xml>' % (frameDescription, frameId)
@@ -828,7 +836,6 @@ class NetCommandFactory:
except:
return self.make_error_message(0, get_exception_traceback_str())
def make_exit_message(self):
try:
net = NetCommand(CMD_EXIT, 0, '')
@@ -105,6 +105,7 @@ USE_LIB_COPY = SUPPORT_GEVENT and \
INTERACTIVE_MODE_AVAILABLE = sys.platform in ('darwin', 'win32') or os.getenv('DISPLAY') is not None
SHOW_CYTHON_WARNING = False
def protect_libraries_from_patching():
@@ -3,7 +3,7 @@
import os
import sys
from _pydevd_bundle.pydevd_constants import CYTHON_SUPPORTED
from _pydevd_bundle.pydevd_constants import CYTHON_SUPPORTED, SHOW_CYTHON_WARNING
use_cython = os.getenv('PYDEVD_USE_CYTHON', None)
@@ -34,6 +34,7 @@ def delete_old_compiled_extensions():
log_error_once("warning: failed to delete old cython speedups. Please delete all *.so files from the directories "
"\"%s\" and \"%s\"" % (_pydevd_bundle_dir, _pydevd_frame_eval_dir))
show_tracing_warning = False
if use_cython == 'YES':
# We must import the cython version if forcing cython
@@ -65,8 +66,11 @@ elif use_cython is None:
from _pydevd_bundle.pydevd_trace_dispatch_regular import trace_dispatch, global_cache_skips, global_cache_frame_skips # @UnusedImport
from _pydev_bundle.pydev_monkey import log_error_once
log_error_once("warning: Debugger speedups using cython not found. Run '\"%s\" \"%s\" build_ext --inplace' to build." % (
sys.executable, os.path.join(dirname, 'setup_cython.py')))
if SHOW_CYTHON_WARNING:
log_error_once("warning: Debugger speedups using cython not found. Run '\"%s\" \"%s\" build_ext --inplace' to build." % (
sys.executable, os.path.join(dirname, 'setup_cython.py')))
else:
show_tracing_warning = True
else:
raise RuntimeError('Unexpected value for PYDEVD_USE_CYTHON: %s (accepted: YES, NO)' % (use_cython,))
@@ -1,6 +1,8 @@
import os
import sys
from _pydevd_bundle.pydevd_constants import SHOW_CYTHON_WARNING
IS_PY36 = False
if sys.version_info[0] == 3 and sys.version_info[1] == 6:
IS_PY36 = True
@@ -9,6 +11,7 @@ frame_eval_func = None
stop_frame_eval = None
enable_cache_frames_without_breaks = None
dummy_trace_dispatch = None
show_frame_eval_warning = False
USE_FRAME_EVAL = os.environ.get('PYDEVD_USE_FRAME_EVAL', None)
@@ -24,5 +27,9 @@ else:
from _pydev_bundle.pydev_monkey import log_error_once
dirname = os.path.dirname(os.path.dirname(__file__))
log_error_once("warning: Debugger speedups using cython not found. Run '\"%s\" \"%s\" build_ext --inplace' to build." % (
sys.executable, os.path.join(dirname, 'setup_cython.py')))
if SHOW_CYTHON_WARNING:
log_error_once("warning: Debugger speedups using cython not found. Run '\"%s\" \"%s\" build_ext --inplace' to build." % (
sys.executable, os.path.join(dirname, 'setup_cython.py')))
else:
show_frame_eval_warning = True
+7 -2
View File
@@ -35,8 +35,9 @@ from _pydevd_bundle.pydevd_comm import CMD_SET_BREAK, CMD_SET_NEXT_STATEMENT, CM
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame
from _pydevd_bundle.pydevd_kill_all_pydevd_threads import kill_all_pydev_threads
from _pydevd_bundle.pydevd_trace_dispatch import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips
from _pydevd_frame_eval.pydevd_frame_eval_main import frame_eval_func, stop_frame_eval, enable_cache_frames_without_breaks, dummy_trace_dispatch
from _pydevd_bundle.pydevd_trace_dispatch import trace_dispatch as _trace_dispatch, global_cache_skips, global_cache_frame_skips, show_tracing_warning
from _pydevd_frame_eval.pydevd_frame_eval_main import frame_eval_func, stop_frame_eval, enable_cache_frames_without_breaks, \
dummy_trace_dispatch, show_frame_eval_warning
from _pydevd_bundle.pydevd_utils import save_main_module
from pydevd_concurrency_analyser.pydevd_concurrency_logger import ThreadingLogger, AsyncioLogger, send_message, cur_time
from pydevd_concurrency_analyser.pydevd_thread_wrappers import wrap_threads
@@ -905,6 +906,10 @@ class PyDB:
# we need all data to be sent to IDE even after program finishes
CheckOutputThread(self).start()
if show_tracing_warning or show_frame_eval_warning:
cmd = self.cmd_factory.make_show_cython_warning_message()
self.writer.add_command(cmd)
def patch_threads(self):
try:
@@ -39,5 +39,7 @@ public interface IPyDebugProcess extends PyFrameAccessor {
void consoleInputRequested(boolean isStarted);
void showCythonWarning();
XDebugSession getSession();
}
@@ -55,6 +55,7 @@ public abstract class AbstractCommand<T> {
public static final int INPUT_REQUESTED = 147;
public static final int PROCESS_CREATED = 149;
public static final int SHOW_CYTHON_WARNING = 150;
public static final int ERROR = 901;
@@ -206,6 +207,10 @@ public abstract class AbstractCommand<T> {
return command == INPUT_REQUESTED;
}
public static boolean isShowWarningCommand(final int command) {
return command == SHOW_CYTHON_WARNING;
}
public static boolean isExitEvent(final int command) {
return command == EXIT;
}
@@ -474,6 +474,9 @@ public class RemoteDebugger implements ProcessDebugger {
else if (ProcessCreatedCommand.isProcessCreatedCommand(frame.getCommand())) {
onProcessCreatedEvent();
}
else if (AbstractCommand.isShowWarningCommand(frame.getCommand())) {
myDebugProcess.showCythonWarning();
}
else {
placeResponse(frame.getSequence(), frame);
}
@@ -22,7 +22,6 @@ import com.intellij.execution.filters.TextConsoleBuilder;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.jetbrains.python.debugger.PyRunCythonExtensionsFilter;
import com.jetbrains.python.run.PythonTracebackFilter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -53,7 +52,6 @@ public class PyDebugConsoleBuilder extends TextConsoleBuilder {
protected ConsoleView createConsole() {
PythonDebugLanguageConsoleView consoleView = new PythonDebugLanguageConsoleView(myProject, mySdk);
consoleView.addMessageFilter(new PythonTracebackFilter(myProject));
consoleView.addMessageFilter(new PyRunCythonExtensionsFilter(myProject));
return consoleView;
}
@@ -0,0 +1,164 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.process.*;
import com.intellij.execution.util.ExecUtil;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.sdk.PythonEnvUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.HyperlinkEvent;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class PyCythonExtensionWarning {
private static final Logger LOG = Logger.getInstance("com.jetbrains.python.debugger.PyCythonExtensionWarning");
public static final String ERROR_TITLE = "Compile Cython Extensions Error";
private static final String CYTHON_WARNING_GROUP_ID = "CythonWarning";
private static final String WARNING_MESSAGE = "Cython extension speed ups Python debugging <br/>" +
"<a href=\"install\">Install</a> " +
"<a href=\"docs\">How does it work</a>";
public static final String SETUP_CYTHON_PATH = "pydev/setup_cython.py";
public static final String[] CYTHON_ARGS = {"build_ext", "--inplace"};
public static void showCythonExtensionWarning(@NotNull Project project) {
Notification notification =
new Notification(CYTHON_WARNING_GROUP_ID, "Python Debugger Extension Available", WARNING_MESSAGE,
NotificationType.INFORMATION, createListener(project));
notification.notify(project);
}
private static NotificationListener.Adapter createListener(@NotNull Project project) {
return new NotificationListener.Adapter() {
@Override
protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
switch (event.getDescription()) {
case "install":
compileCythonExtension(project);
notification.expire();
return;
case "docs":
// TODO: help id for cython speedups help page
HelpManager.getInstance().invokeHelp("");
}
}
};
}
private static void showErrorDialog(Project project, String message) {
Messages.showMessageDialog(project, message, ERROR_TITLE, null);
}
private static void compileCythonExtension(@NotNull Project project) {
try {
final RunManager runManager = RunManager.getInstance(project);
final RunnerAndConfigurationSettings selectedConfiguration = runManager.getSelectedConfiguration();
if (selectedConfiguration == null) {
throw new ExecutionException("Python Run Configuration should be selected");
}
final RunConfiguration configuration = selectedConfiguration.getConfiguration();
if (!(configuration instanceof AbstractPythonRunConfiguration)) {
throw new ExecutionException("Python Run Configuration should be selected");
}
AbstractPythonRunConfiguration runConfiguration = (AbstractPythonRunConfiguration)configuration;
final String sdkPath = runConfiguration.getSdkHome();
final String helpersPath = PythonHelpersLocator.getHelpersRoot().getPath();
final List<String> cmdline = new ArrayList<>();
cmdline.add(sdkPath);
cmdline.add(FileUtil.join(helpersPath, FileUtil.toSystemDependentName(SETUP_CYTHON_PATH)));
cmdline.addAll(Arrays.asList(CYTHON_ARGS));
LOG.info("Compile Cython Extensions " + StringUtil.join(cmdline, " "));
final Map<String, String> environment = new HashMap<>(System.getenv());
PythonEnvUtil.setPythonUnbuffered(environment);
PythonEnvUtil.setPythonDontWriteBytecode(environment);
if (sdkPath != null) {
PythonEnvUtil.resetHomePathChanges(sdkPath, environment);
}
GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withEnvironment(environment);
final boolean canCreate = FileUtil.ensureCanCreateFile(new File(helpersPath));
final boolean useSudo = !canCreate && !SystemInfo.isWindows;
Process process;
if (useSudo) {
process = ExecUtil.sudo(commandLine, "Please enter your password to compile cython extensions: ");
}
else {
process = commandLine.createProcess();
}
ProgressManager.getInstance().run(new Task.Backgroundable(project, "Compile Cython Extensions") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
final CapturingProcessHandler handler =
new CapturingProcessHandler(process, commandLine.getCharset(), commandLine.getCommandLineString());
handler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
if (outputType == ProcessOutputTypes.STDOUT || outputType == ProcessOutputTypes.STDERR) {
for (String line : StringUtil.splitByLines(event.getText())) {
if (isSignificantOutput(line)) {
indicator.setText2(line.trim());
}
}
}
}
private boolean isSignificantOutput(String line) {
return line.trim().length() > 3;
}
});
final ProcessOutput result = handler.runProcessWithProgressIndicator(indicator);
final int exitCode = result.getExitCode();
if (exitCode != 0) {
final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr())
? "Permission denied"
: "Non-zero exit code (" + exitCode + "): \n" + result.getStderr();
UIUtil.invokeLaterIfNeeded(() -> showErrorDialog(project, message));
}
}
});
}
catch (IOException | ExecutionException e) {
showErrorDialog(project, e.getMessage());
}
}
}
@@ -119,6 +119,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
private PyStackFrame myConsoleContextFrame = null;
private PyReferrersLoader myReferrersProvider;
private final List<PyFrameListener> myFrameListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private boolean isCythonWarningShown = false;
public PyDebugProcess(@NotNull XDebugSession session,
@NotNull ServerSocket serverSocket,
@@ -382,6 +383,14 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
}
}
@Override
public void showCythonWarning() {
if (!isCythonWarningShown) {
PyCythonExtensionWarning.showCythonExtensionWarning(getSession().getProject());
isCythonWarningShown = true;
}
}
protected void afterConnect() {
}
@@ -1,161 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.process.*;
import com.intellij.execution.util.ExecUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.sdk.PythonEnvUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class PyRunCythonExtensionsFilter implements Filter {
public static final String ERROR_TITLE = "Compile Cython Extensions Error";
public static final String WARNING_MESSAGE_BEGIN = "warning: Debugger speedups using cython not found. Run \'";
public static final String WARNING_MESSAGE_END = "\' to build.";
public static final String SETUP_CYTHON_PATH = "pydev/setup_cython.py";
public static final String[] CYTHON_ARGS = {"build_ext", "--inplace"};
private static final Logger LOG = Logger.getInstance("com.jetbrains.python.debugger.PyRunCythonExtensionsFilter");
private final @NotNull Project myProject;
private volatile boolean isApplied = false;
public PyRunCythonExtensionsFilter(@NotNull Project project) {
myProject = project;
}
@Nullable
@Override
public Result applyFilter(String line, int entireLength) {
final int indexOfWarning = line.indexOf(WARNING_MESSAGE_BEGIN);
if (indexOfWarning != -1) {
final int indexOfLinkBeginning = indexOfWarning + WARNING_MESSAGE_BEGIN.length();
final int textStartOffset = entireLength - line.length();
final int indexOfLinkEnd = line.indexOf(WARNING_MESSAGE_END);
return new Result(textStartOffset + indexOfLinkBeginning, textStartOffset + indexOfLinkEnd, new DebuggerExtensionsHyperlinkInfo());
}
return null;
}
private void showErrorDialog(String message) {
Messages.showMessageDialog(myProject, message, ERROR_TITLE, null);
}
private class DebuggerExtensionsHyperlinkInfo implements HyperlinkInfo {
@Override
public void navigate(Project project) {
try {
if (isApplied) {
return;
}
final RunManager runManager = RunManager.getInstance(myProject);
final RunnerAndConfigurationSettings selectedConfiguration = runManager.getSelectedConfiguration();
if (selectedConfiguration == null) {
throw new ExecutionException("Python Run Configuration should be selected");
}
final RunConfiguration configuration = selectedConfiguration.getConfiguration();
if (!(configuration instanceof AbstractPythonRunConfiguration)) {
throw new ExecutionException("Python Run Configuration should be selected");
}
AbstractPythonRunConfiguration runConfiguration = (AbstractPythonRunConfiguration)configuration;
final String sdkPath = runConfiguration.getSdkHome();
final String helpersPath = PythonHelpersLocator.getHelpersRoot().getPath();
final List<String> cmdline = new ArrayList<>();
cmdline.add(sdkPath);
cmdline.add(FileUtil.join(helpersPath, FileUtil.toSystemDependentName(SETUP_CYTHON_PATH)));
cmdline.addAll(Arrays.asList(CYTHON_ARGS));
LOG.info("Compile Cython Extensions " + StringUtil.join(cmdline, " "));
final Map<String, String> environment = new HashMap<>(System.getenv());
PythonEnvUtil.setPythonUnbuffered(environment);
PythonEnvUtil.setPythonDontWriteBytecode(environment);
if (sdkPath != null) {
PythonEnvUtil.resetHomePathChanges(sdkPath, environment);
}
GeneralCommandLine commandLine = new GeneralCommandLine(cmdline).withEnvironment(environment);
final boolean canCreate = FileUtil.ensureCanCreateFile(new File(helpersPath));
final boolean useSudo = !canCreate && !SystemInfo.isWindows;
Process process;
if (useSudo) {
process = ExecUtil.sudo(commandLine, "Please enter your password to compile cython extensions: ");
}
else {
process = commandLine.createProcess();
}
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, "Compile Cython Extensions") {
@Override
public void run(@NotNull ProgressIndicator indicator) {
isApplied = true;
final CapturingProcessHandler handler =
new CapturingProcessHandler(process, commandLine.getCharset(), commandLine.getCommandLineString());
handler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
if (outputType == ProcessOutputTypes.STDOUT || outputType == ProcessOutputTypes.STDERR) {
for (String line : StringUtil.splitByLines(event.getText())) {
if (isSignificantOutput(line)) {
indicator.setText2(line.trim());
}
}
}
}
private boolean isSignificantOutput(String line) {
return line.trim().length() > 3;
}
});
final ProcessOutput result = handler.runProcessWithProgressIndicator(indicator);
final int exitCode = result.getExitCode();
if (exitCode != 0) {
final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr())
? "Permission denied"
: "Non-zero exit code (" + exitCode + "): \n" + result.getStderr();
UIUtil.invokeLaterIfNeeded(() -> showErrorDialog(message));
}
}
});
}
catch (IOException | ExecutionException e) {
showErrorDialog(e.getMessage());
}
}
}
}