From 8bf415c1041f83adfa0fb7da250f7842ac01ae1d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Mon, 5 Apr 2010 21:53:49 +0400 Subject: [PATCH 01/17] http://ea.jetbrains.com/browser/ea_problems/19719 --- .../com/jetbrains/python/psi/resolve/ResolveImportUtil.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 1cb0ed9896a3..4229a46b9775 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -538,6 +538,9 @@ public class ResolveImportUtil { public boolean visitRoot(final VirtualFile root) { PsiElement module = root.isDirectory() ? psimgr.findDirectory(root) : psimgr.findFile(root); for (String component : qualifiedName.getComponents()) { + if (component == null) { + break; + } module = resolveChild(module, component, foothold_file, false); // only files, we want a module } if (module != null) { From 53081f69fb46dbd702b7ab1d60a56f8437e93667 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Mon, 5 Apr 2010 21:54:48 +0400 Subject: [PATCH 02/17] correct fix --- .../src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index 4229a46b9775..9e457c553622 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -539,7 +539,7 @@ public class ResolveImportUtil { PsiElement module = root.isDirectory() ? psimgr.findDirectory(root) : psimgr.findFile(root); for (String component : qualifiedName.getComponents()) { if (component == null) { - break; + return true; } module = resolveChild(module, component, foothold_file, false); // only files, we want a module } From 49e89f237e2b52270c34233bfc85251b4dbbc843 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 14:33:27 +0400 Subject: [PATCH 03/17] split SDK type into multiple flavors --- .../jetbrains/python/sdk/JythonSdkFlavor.java | 26 ++++ .../python/sdk/MacPythonSdkFlavor.java | 46 +++++++ .../jetbrains/python/sdk/PythonSdkFlavor.java | 61 ++++++++++ .../jetbrains/python/sdk/PythonSdkType.java | 115 +++--------------- .../python/sdk/UnixPythonSdkFlavor.java | 36 ++++++ .../python/sdk/WinPythonSdkFlavor.java | 42 +++++++ 6 files changed, 225 insertions(+), 101 deletions(-) create mode 100644 python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java create mode 100644 python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java create mode 100644 python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java create mode 100644 python/src/com/jetbrains/python/sdk/UnixPythonSdkFlavor.java create mode 100644 python/src/com/jetbrains/python/sdk/WinPythonSdkFlavor.java diff --git a/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java new file mode 100644 index 000000000000..4c4af7f4a48b --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java @@ -0,0 +1,26 @@ +package com.jetbrains.python.sdk; + +import com.intellij.openapi.util.io.FileUtil; + +import java.io.File; + +/** + * @author yole + */ +public class JythonSdkFlavor extends PythonSdkFlavor { + private JythonSdkFlavor() { + } + + public static JythonSdkFlavor INSTANCE = new JythonSdkFlavor(); + + @Override + public boolean isValidSdkHome(String path) { + File file = new File(path); + return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("jython"); + } + + @Override + public String getVersionString(String sdkHome) { + return getVersionFromOutput(sdkHome, "--version", "(Jython \\S+) on .*"); + } +} diff --git a/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java new file mode 100644 index 000000000000..ca49790dbc0d --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/MacPythonSdkFlavor.java @@ -0,0 +1,46 @@ +package com.jetbrains.python.sdk; + +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yole + */ +public class MacPythonSdkFlavor extends PythonSdkFlavor { + private MacPythonSdkFlavor() { + } + + public static MacPythonSdkFlavor INSTANCE = new MacPythonSdkFlavor(); + + @Override + public List suggestHomePaths() { + List candidates = new ArrayList(); + final String pythonPath = "/Library/Frameworks/Python.framework/Versions"; + VirtualFile rootVDir = LocalFileSystem.getInstance().findFileByPath(pythonPath); + if (rootVDir != null) { + for (VirtualFile dir : rootVDir.getChildren()) { + final String dir_name = dir.getName().toLowerCase(); + if (dir.isDirectory()) { + // TODO why would Jython be ever installed under /Library/Frameworks? + if ("Current".equals(dir_name) || dir_name.startsWith("2") || dir_name.startsWith("3") || dir_name.startsWith("jython")) { + VirtualFile bin_dir = dir.findChild("bin"); + if (bin_dir != null && bin_dir.isDirectory()) { + VirtualFile python_exe = dir.findChild("python"); + if (python_exe != null) candidates.add(python_exe.getPath()); + python_exe = dir.findChild("jython"); // maybe it's in bin/ + if (python_exe != null) candidates.add(python_exe.getPath()); + } + else { + VirtualFile python_exe = dir.findChild("jython"); // maybe it's not in bin/ + if (python_exe != null) candidates.add(python_exe.getPath()); + } + } + } + } + } + return candidates; + } +} diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java new file mode 100644 index 000000000000..54d1a5ab1f07 --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -0,0 +1,61 @@ +package com.jetbrains.python.sdk; + +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +/** + * @author yole + */ +public abstract class PythonSdkFlavor { + public List suggestHomePaths() { + return Collections.emptyList(); + } + + /** + * Checks if the path is the name of a Python intepreter of this flavor. + * + * @param path path to check. + * @return true if paths points to a valid home. + */ + public boolean isValidSdkHome(String path) { + File file = new File(path); + return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("python"); + } + + public static List getApplicableFlavors() { + List result = new ArrayList(); + if (SystemInfo.isWindows) { + result.add(WinPythonSdkFlavor.INSTANCE); + } + else if (SystemInfo.isMac) { + result.add(MacPythonSdkFlavor.INSTANCE); + } + else if (SystemInfo.isUnix) { + result.add(UnixPythonSdkFlavor.INSTANCE); + } + result.add(JythonSdkFlavor.INSTANCE); + return result; + } + + public String getVersionString(String sdkHome) { + return getVersionFromOutput(sdkHome, "-V", "(Python \\S+).*"); + } + + protected static String getVersionFromOutput(String sdkHome, String version_opt, String version_regexp) { + Pattern pattern = Pattern.compile(version_regexp); + String run_dir = new File(sdkHome).getParent(); + final ProcessOutput process_output = SdkUtil.getProcessOutput(run_dir, new String[]{sdkHome, version_opt}); + if (process_output.getExitCode() != 0) { + throw new RuntimeException(process_output.getStderr() + " (exit code " + process_output.getExitCode() + ")"); + } + return SdkUtil.getFirstMatch(process_output.getStderrLines(), pattern); + } + +} diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index b628bacab24b..74cf38b1c539 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -52,7 +52,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.TreeSet; -import java.util.regex.Pattern; import static com.jetbrains.python.psi.PyUtil.sure; @@ -109,49 +108,8 @@ public class PythonSdkType extends SdkType { return findDigits(o1).compareTo(findDigits(o2)); } }); - if (SystemInfo.isWindows) { - findSubdirInstallations(candidates, "C:\\", PYTHON_STR, "python.exe"); - findSubdirInstallations(candidates, "C:\\Program Files\\", PYTHON_STR, "python.exe"); - findSubdirInstallations(candidates, "C:\\", "jython", "jython.bat"); - } - else if (SystemInfo.isMac) { - final String pythonPath = "/Library/Frameworks/Python.framework/Versions"; - VirtualFile rootVDir = LocalFileSystem.getInstance().findFileByPath(pythonPath); - if (rootVDir != null) { - for (VirtualFile dir : rootVDir.getChildren()) { - final String dir_name = dir.getName().toLowerCase(); - if (dir.isDirectory()) { - if ("Current".equals(dir_name) || dir_name.startsWith("2") || dir_name.startsWith("3") || dir_name.startsWith("jython")) { - VirtualFile bin_dir = dir.findChild("bin"); - if (bin_dir != null && bin_dir.isDirectory()) { - VirtualFile python_exe = dir.findChild(PYTHON_STR); - if (python_exe != null) candidates.add(python_exe.getPath()); - python_exe = dir.findChild("jython"); // maybe it's in bin/ - if (python_exe != null) candidates.add(python_exe.getPath()); - } - else { - VirtualFile python_exe = dir.findChild("jython"); // maybe it's not in bin/ - if (python_exe != null) candidates.add(python_exe.getPath()); - } - } - } - } - } - } - else if (SystemInfo.isUnix) { - VirtualFile rootDir = LocalFileSystem.getInstance().findFileByPath("/usr/bin"); - if (rootDir != null) { - VirtualFile[] suspects = rootDir.getChildren(); - for (VirtualFile child : suspects) { - if (!child.isDirectory()) { - final String child_name = child.getName(); - if (child_name.startsWith(PYTHON_STR) || child_name.startsWith("jython")) { - candidates.add(child.getPath()); - } - } - } - candidates.add(rootDir.getPath()); - } + for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) { + candidates.addAll(flavor.suggestHomePaths()); } if (candidates.size() > 0) { @@ -174,20 +132,18 @@ public class PythonSdkType extends SdkType { return s; } - private static void findSubdirInstallations(TreeSet candidates, String rootDir, String dir_prefix, String exe_name) { - VirtualFile rootVDir = LocalFileSystem.getInstance().findFileByPath("C:\\"); - if (rootVDir != null) { - for (VirtualFile dir : rootVDir.getChildren()) { - if (dir.isDirectory() && dir.getName().toLowerCase().startsWith(dir_prefix)) { - VirtualFile python_exe = dir.findChild(exe_name); - if (python_exe != null) candidates.add(python_exe.getPath()); - } - } - } + public boolean isValidSdkHome(final String path) { + return getFlavor(path) != null; } - public boolean isValidSdkHome(final String path) { - return isPythonSdkHome(path) || isJythonSdkHome(path); + @Nullable + private static PythonSdkFlavor getFlavor(String sdkPath) { + for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) { + if (flavor.isValidSdkHome(sdkPath)) { + return flavor; + } + } + return null; } @Override @@ -225,29 +181,6 @@ public class PythonSdkType extends SdkType { return result; } - /** - * Checks if the path is the name of a Python intepreter. - * - * @param path path to check. - * @return true if paths points to a valid home. - */ - @NonNls - private static boolean isPythonSdkHome(final String path) { - File file = new File(path); - return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("python"); - } - - /** - * Checks if the path is the name of a Jython intepreter. - * - * @param path path to check. - * @return true if paths points to a valid home. - */ - private static boolean isJythonSdkHome(final String path) { - File file = new File(path); - return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("jython"); - } - /** * @param path where to look * @return Python interpreter executable on the path, or null. @@ -639,28 +572,8 @@ public class PythonSdkType extends SdkType { @Nullable public String getVersionString(final String sdkHome) { - final String binaryPath = getInterpreterPath(sdkHome); - if (binaryPath == null) { - return null; - } - final boolean isJython = isJythonSdkHome(sdkHome); - @NonNls String version_regexp, version_opt; - if (isJython) { - version_regexp = "(Jython \\S+) on .*"; - version_opt = "--version"; - } - else { // CPython - version_regexp = "(Python \\S+).*"; - version_opt = "-V"; - } - Pattern pattern = Pattern.compile(version_regexp); - String run_dir = new File(binaryPath).getParent(); - final ProcessOutput process_output = SdkUtil.getProcessOutput(run_dir, new String[]{binaryPath, version_opt}); - if (process_output.getExitCode() != 0) { - throw new RuntimeException(process_output.getStderr() + " (exit code " + process_output.getExitCode() + ")"); - } - String version = SdkUtil.getFirstMatch(process_output.getStderrLines(), pattern); - return version; + final PythonSdkFlavor flavor = getFlavor(sdkHome); + return flavor != null ? flavor.getVersionString(sdkHome) : null; } @Nullable diff --git a/python/src/com/jetbrains/python/sdk/UnixPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/UnixPythonSdkFlavor.java new file mode 100644 index 000000000000..1e5e41d6badb --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/UnixPythonSdkFlavor.java @@ -0,0 +1,36 @@ +package com.jetbrains.python.sdk; + +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yole + */ +public class UnixPythonSdkFlavor extends PythonSdkFlavor { + private UnixPythonSdkFlavor() { + } + + public static UnixPythonSdkFlavor INSTANCE = new UnixPythonSdkFlavor(); + + @Override + public List suggestHomePaths() { + List candidates = new ArrayList(); + VirtualFile rootDir = LocalFileSystem.getInstance().findFileByPath("/usr/bin"); + if (rootDir != null) { + VirtualFile[] suspects = rootDir.getChildren(); + for (VirtualFile child : suspects) { + if (!child.isDirectory()) { + final String child_name = child.getName(); + if (child_name.startsWith("python") || child_name.startsWith("jython")) { + candidates.add(child.getPath()); + } + } + } + candidates.add(rootDir.getPath()); + } + return candidates; + } +} diff --git a/python/src/com/jetbrains/python/sdk/WinPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/WinPythonSdkFlavor.java new file mode 100644 index 000000000000..cdafb46b093a --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/WinPythonSdkFlavor.java @@ -0,0 +1,42 @@ +package com.jetbrains.python.sdk; + +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NonNls; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author yole + */ +public class WinPythonSdkFlavor extends PythonSdkFlavor { + @NonNls static final private String PYTHON_STR = "python"; + + public static WinPythonSdkFlavor INSTANCE = new WinPythonSdkFlavor(); + + private WinPythonSdkFlavor() { + } + + @Override + public List suggestHomePaths() { + List candidates = new ArrayList(); + findSubdirInstallations(candidates, "C:\\", PYTHON_STR, "python.exe"); + findSubdirInstallations(candidates, "C:\\Program Files\\", PYTHON_STR, "python.exe"); + findSubdirInstallations(candidates, "C:\\", "jython", "jython.bat"); + return candidates; + } + + private static void findSubdirInstallations(Collection candidates, String rootDir, String dir_prefix, String exe_name) { + VirtualFile rootVDir = LocalFileSystem.getInstance().findFileByPath(rootDir); + if (rootVDir != null) { + for (VirtualFile dir : rootVDir.getChildren()) { + if (dir.isDirectory() && dir.getName().toLowerCase().startsWith(dir_prefix)) { + VirtualFile python_exe = dir.findChild(exe_name); + if (python_exe != null) candidates.add(python_exe.getPath()); + } + } + } + } +} From 9905b5950ef8bf23cf788200e80e1648adf0f1d2 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 14:45:18 +0400 Subject: [PATCH 04/17] SDK flavor for IronPython --- .../python/sdk/IronPythonSdkFlavor.java | 24 ++++++++++++++ .../jetbrains/python/sdk/JythonSdkFlavor.java | 2 +- .../jetbrains/python/sdk/PythonSdkFlavor.java | 33 ++++++++++--------- 3 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java diff --git a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java new file mode 100644 index 000000000000..328cf7bd1aa1 --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java @@ -0,0 +1,24 @@ +package com.jetbrains.python.sdk; + +import java.io.File; + +/** + * @author yole + */ +public class IronPythonSdkFlavor extends PythonSdkFlavor { + private IronPythonSdkFlavor() { + } + + public static IronPythonSdkFlavor INSTANCE = new IronPythonSdkFlavor(); + + @Override + public boolean isValidSdkHome(String path) { + final String name = new File(path).getName(); + return name.equals("ipy.exe") || name.equals("ipy64.exe"); + } + + @Override + public String getVersionString(String sdkHome) { + return "IronPython " + getVersionFromOutput(sdkHome, "-V", "\\w+ ([0-9\\.]+).*", true); + } +} diff --git a/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java index 4c4af7f4a48b..99fce6d179ef 100644 --- a/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/JythonSdkFlavor.java @@ -21,6 +21,6 @@ public class JythonSdkFlavor extends PythonSdkFlavor { @Override public String getVersionString(String sdkHome) { - return getVersionFromOutput(sdkHome, "--version", "(Jython \\S+) on .*"); + return getVersionFromOutput(sdkHome, "--version", "(Jython \\S+) on .*", false); } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index 54d1a5ab1f07..4ec77255bf32 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -18,17 +18,6 @@ public abstract class PythonSdkFlavor { return Collections.emptyList(); } - /** - * Checks if the path is the name of a Python intepreter of this flavor. - * - * @param path path to check. - * @return true if paths points to a valid home. - */ - public boolean isValidSdkHome(String path) { - File file = new File(path); - return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("python"); - } - public static List getApplicableFlavors() { List result = new ArrayList(); if (SystemInfo.isWindows) { @@ -41,21 +30,33 @@ public abstract class PythonSdkFlavor { result.add(UnixPythonSdkFlavor.INSTANCE); } result.add(JythonSdkFlavor.INSTANCE); + result.add(IronPythonSdkFlavor.INSTANCE); return result; } - public String getVersionString(String sdkHome) { - return getVersionFromOutput(sdkHome, "-V", "(Python \\S+).*"); + /** + * Checks if the path is the name of a Python interpreter of this flavor. + * + * @param path path to check. + * @return true if paths points to a valid home. + */ + public boolean isValidSdkHome(String path) { + File file = new File(path); + return file.isFile() && FileUtil.getNameWithoutExtension(file).toLowerCase().startsWith("python"); } - protected static String getVersionFromOutput(String sdkHome, String version_opt, String version_regexp) { + public String getVersionString(String sdkHome) { + return getVersionFromOutput(sdkHome, "-V", "(Python \\S+).*", false); + } + + protected static String getVersionFromOutput(String sdkHome, String version_opt, String version_regexp, boolean stdout) { Pattern pattern = Pattern.compile(version_regexp); String run_dir = new File(sdkHome).getParent(); final ProcessOutput process_output = SdkUtil.getProcessOutput(run_dir, new String[]{sdkHome, version_opt}); if (process_output.getExitCode() != 0) { throw new RuntimeException(process_output.getStderr() + " (exit code " + process_output.getExitCode() + ")"); } - return SdkUtil.getFirstMatch(process_output.getStderrLines(), pattern); + final List lines = stdout ? process_output.getStdoutLines() : process_output.getStderrLines(); + return SdkUtil.getFirstMatch(lines, pattern); } - } From ea8f4516bd0e0707c4190cabbf5850a60f4eac5f Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 15:34:13 +0400 Subject: [PATCH 05/17] bump the timeout even further --- python/src/com/jetbrains/python/sdk/PythonSdkType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 74cf38b1c539..dcdb6ee9be88 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -62,7 +62,7 @@ public class PythonSdkType extends SdkType { private static final Logger LOG = Logger.getInstance("#" + PythonSdkType.class.getName()); private static final String[] WINDOWS_EXECUTABLE_SUFFIXES = new String[]{"cmd", "exe", "bat", "com"}; - static final int RUN_TIMEOUT = 30 * 1000; // 30 seconds per script invocation is plenty; anything more seems wrong (10 wasn't enough tho). + static final int RUN_TIMEOUT = 60 * 1000; // 60 seconds per script invocation is plenty; anything more seems wrong (10 wasn't enough tho). public static PythonSdkType getInstance() { return SdkType.findInstance(PythonSdkType.class); From dabd11434897870dfc718502788892bcd3d4b930 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 15:48:39 +0400 Subject: [PATCH 06/17] escape line breaks in object repr values --- python/helpers/generator3.py | 2 +- python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index 16704fb68287..7ed71ea5fe50 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -613,7 +613,7 @@ class ModuleRedeclarator(object): else: # a forward / circular declaration happens notice = "" - s = repr(p_value) + s = repr(p_value).replace("\n", "\\n").replace("\r", "\\r") if found_name: if found_name == as_name: notice = " # (!) real value is " + s diff --git a/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java b/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java index 0ce7d853df0c..1e7f31cd9795 100644 --- a/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java +++ b/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java @@ -18,7 +18,7 @@ import java.util.List; public class SkeletonVersionUpdater implements ProjectComponent { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.sdk.SkeletonVersionUpdater"); - public static int SKELETONS_VERSION = 2; + public static int SKELETONS_VERSION = 3; public SkeletonVersionUpdater(StartupManager startupManager) { startupManager.registerStartupActivity(new Runnable() { From 56325a9cd1a41cbfb19d50448d750b8de4b9a64e Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 16:13:47 +0400 Subject: [PATCH 07/17] initial debugging support for IronPython --- .../jetbrains/python/debugger/PyDebugRunner.java | 9 +++++++++ .../run/AbstractPythonRunConfiguration.java | 7 +++++++ .../python/sdk/IronPythonSdkFlavor.java | 7 +++++++ .../jetbrains/python/sdk/PythonSdkFlavor.java | 16 ++++++++++++++++ .../com/jetbrains/python/sdk/PythonSdkType.java | 14 ++------------ 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java index 75dc0f001b47..2b864d404aa2 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugRunner.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugRunner.java @@ -22,10 +22,12 @@ import com.jetbrains.python.run.AbstractPythonRunConfiguration; import com.jetbrains.python.run.CommandLinePatcher; import com.jetbrains.python.run.PythonCommandLineState; import com.jetbrains.python.run.PythonRunConfiguration; +import com.jetbrains.python.sdk.PythonSdkFlavor; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.net.ServerSocket; +import java.util.Collection; /** * @author yole @@ -66,6 +68,13 @@ public class PyDebugRunner extends GenericProgramRunner { // script name is the last parameter; all other params are for python interpreter; insert just before name final ParametersList parameters_list = commandLine.getParametersList(); int parameter_offset = pyState.getInterpreterOptionsCount(); + final PythonSdkFlavor flavor = pyState.getConfig().getSdkFlavor(); + if (flavor != null) { + final Collection options = flavor.getExtraDebugOptions(); + for (String option : options) { + parameters_list.addAt(parameter_offset++, option); + } + } for (int i = 0; i < args.length; i++) { parameters_list.addAt(i + parameter_offset, args[i]); } diff --git a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java index e4eb48d6ade9..25313797464e 100644 --- a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java @@ -14,6 +14,7 @@ import com.intellij.openapi.util.JDOMExternalizerUtil; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.sdk.PythonSdkFlavor; import com.jetbrains.python.sdk.PythonSdkType; import org.jdom.Element; import org.jetbrains.annotations.Nullable; @@ -108,6 +109,12 @@ public abstract class AbstractPythonRunConfiguration extends ModuleBasedConfigur return PythonSdkType.getInterpreterPath(sdkHome); } + @Nullable + public PythonSdkFlavor getSdkFlavor() { + final String path = getInterpreterPath(); + return path == null ? null : PythonSdkFlavor.getFlavor(path); + } + public void readExternal(Element element) throws InvalidDataException { super.readExternal(element); myInterpreterOptions = JDOMExternalizerUtil.readField(element, "INTERPRETER_OPTIONS"); diff --git a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java index 328cf7bd1aa1..a7cdb8b2737d 100644 --- a/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/IronPythonSdkFlavor.java @@ -1,6 +1,8 @@ package com.jetbrains.python.sdk; import java.io.File; +import java.util.Collection; +import java.util.Collections; /** * @author yole @@ -21,4 +23,9 @@ public class IronPythonSdkFlavor extends PythonSdkFlavor { public String getVersionString(String sdkHome) { return "IronPython " + getVersionFromOutput(sdkHome, "-V", "\\w+ ([0-9\\.]+).*", true); } + + @Override + public Collection getExtraDebugOptions() { + return Collections.singletonList("-X:Frames"); + } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index 4ec77255bf32..bdd7fde8014e 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -3,9 +3,11 @@ package com.jetbrains.python.sdk; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; @@ -34,6 +36,16 @@ public abstract class PythonSdkFlavor { return result; } + @Nullable + public static PythonSdkFlavor getFlavor(String sdkPath) { + for (PythonSdkFlavor flavor : getApplicableFlavors()) { + if (flavor.isValidSdkHome(sdkPath)) { + return flavor; + } + } + return null; + } + /** * Checks if the path is the name of a Python interpreter of this flavor. * @@ -59,4 +71,8 @@ public abstract class PythonSdkFlavor { final List lines = stdout ? process_output.getStdoutLines() : process_output.getStderrLines(); return SdkUtil.getFirstMatch(lines, pattern); } + + public Collection getExtraDebugOptions() { + return Collections.emptyList(); + } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index dcdb6ee9be88..c04f3fada5fd 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -133,17 +133,7 @@ public class PythonSdkType extends SdkType { } public boolean isValidSdkHome(final String path) { - return getFlavor(path) != null; - } - - @Nullable - private static PythonSdkFlavor getFlavor(String sdkPath) { - for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) { - if (flavor.isValidSdkHome(sdkPath)) { - return flavor; - } - } - return null; + return PythonSdkFlavor.getFlavor(path) != null; } @Override @@ -572,7 +562,7 @@ public class PythonSdkType extends SdkType { @Nullable public String getVersionString(final String sdkHome) { - final PythonSdkFlavor flavor = getFlavor(sdkHome); + final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdkHome); return flavor != null ? flavor.getVersionString(sdkHome) : null; } From ad58560806c6e8b5f1e98d816fbafa32af5512b8 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 17:40:28 +0400 Subject: [PATCH 08/17] XXL timeout --- python/src/com/jetbrains/python/sdk/PythonSdkType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index c04f3fada5fd..e83e914a47e9 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -588,7 +588,7 @@ public class PythonSdkType extends SdkType { "-b", // for builtins "-u", // for update-only mode }, - getVirtualEnvAdditionalEnv(binary_path), RUN_TIMEOUT + getVirtualEnvAdditionalEnv(binary_path), RUN_TIMEOUT*5 ); if (run_result.getExitCode() != 0) { LOG.error(run_result.getStderr() + (run_result.isTimeout()? "\nTimed out" : "\nExit code " + run_result.getExitCode())); From 947a350191ac8df2174e675a754a48723f24f5d8 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 18:38:19 +0400 Subject: [PATCH 09/17] remove invalid characters from repr() output --- python/helpers/generator3.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index 7ed71ea5fe50..9c87af86b704 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -161,6 +161,15 @@ def sortedNoCase(p_array): return p_array +def cleanup(value): + result = '' + for c in value: + if c == '\n': result += '\\n' + elif c == '\r': result += '\\r' + elif c < ' ' or c > chr(127): result += '?' + else: result += c + return result + _prop_types = [type(property())] try: _prop_types.append(types.GetSetDescriptorType) except: pass @@ -613,7 +622,7 @@ class ModuleRedeclarator(object): else: # a forward / circular declaration happens notice = "" - s = repr(p_value).replace("\n", "\\n").replace("\r", "\\r") + s = cleanup(repr(p_value)) if found_name: if found_name == as_name: notice = " # (!) real value is " + s From 497f04334c790e8d097cc18ea3e9a79ebf6c699d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 18:47:01 +0400 Subject: [PATCH 10/17] option for importing CLR references with specified names --- python/helpers/generator3.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index 9c87af86b704..7cb9260bb093 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -1051,8 +1051,9 @@ if __name__ == "__main__": -q -- quiet, do not print anything on stdout. Errors still go to stderr. -u -- update, only recreate skeletons for newer files, and skip unchanged. -x -- die on exceptions with a stacktrace; only for debugging. + -c modules -- import CLR assemblies with specified names """ - opts, fnames = getopt(sys.argv[1:], "d:hbqux") + opts, fnames = getopt(sys.argv[1:], "d:hbquxc:") opts = dict(opts) if not opts or '-h' in opts: print(helptext) @@ -1075,6 +1076,12 @@ if __name__ == "__main__": names.remove('__main__') # we don't want ourselves processed else: doing_builtins = False + + refs = opts.get('-c', '') + if refs: + import clr + for ref in refs.split(';'): clr.AddReferenceByPartialName(ref) + # go on for name in names: if not quiet: From 20eec380e14213fec09e581838e72b30c3c06dfb Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 20:38:00 +0400 Subject: [PATCH 11/17] built-in support for CLR profiler --- python/helpers/generator3.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index 7cb9260bb093..5a65a4125120 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -24,6 +24,7 @@ import os import string import stat import types +import atexit #import __builtin__ try: @@ -170,6 +171,15 @@ def cleanup(value): else: result += c return result +# http://blogs.msdn.com/curth/archive/2009/03/29/an-ironpython-profiler.aspx +def print_profile(): + import clr + data = [] + data.extend(clr.GetProfilerData()) + data.sort(lambda x, y: -cmp(x.InclusiveTime, y.InclusiveTime)) + for p in data: + print '%s\t%d\t%d\t%d' % (p.Name, p.InclusiveTime, p.ExclusiveTime, p.Calls) + _prop_types = [type(property())] try: _prop_types.append(types.GetSetDescriptorType) except: pass @@ -1052,8 +1062,9 @@ if __name__ == "__main__": -u -- update, only recreate skeletons for newer files, and skip unchanged. -x -- die on exceptions with a stacktrace; only for debugging. -c modules -- import CLR assemblies with specified names + -p -- run CLR profiler """ - opts, fnames = getopt(sys.argv[1:], "d:hbquxc:") + opts, fnames = getopt(sys.argv[1:], "d:hbquxc:p") opts = dict(opts) if not opts or '-h' in opts: print(helptext) @@ -1082,6 +1093,9 @@ if __name__ == "__main__": import clr for ref in refs.split(';'): clr.AddReferenceByPartialName(ref) + if '-p' in opts: + atexit.register(print_profile) + # go on for name in names: if not quiet: From baecd72fe89be90dbaff0ed3356fbb06b1439fd4 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 21:39:04 +0400 Subject: [PATCH 12/17] IronPython-aware skeletons generation (initial) --- python/helpers/generator3.py | 58 ++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/python/helpers/generator3.py b/python/helpers/generator3.py index 5a65a4125120..9dea2f0f1f5a 100644 --- a/python/helpers/generator3.py +++ b/python/helpers/generator3.py @@ -1,5 +1,4 @@ # encoding: utf-8 -import re """ This thing tries to restore public interface of objects that don't have a python source: C extensions and built-in objects. It does not reimplement the @@ -34,6 +33,9 @@ except ImportError: import re +if sys.platform == 'cli': + import clr + string_mod = string version = ( @@ -173,13 +175,20 @@ def cleanup(value): # http://blogs.msdn.com/curth/archive/2009/03/29/an-ironpython-profiler.aspx def print_profile(): - import clr data = [] data.extend(clr.GetProfilerData()) data.sort(lambda x, y: -cmp(x.InclusiveTime, y.InclusiveTime)) for p in data: print '%s\t%d\t%d\t%d' % (p.Name, p.InclusiveTime, p.ExclusiveTime, p.Calls) +def is_clr_type(t): + if not t: return False + try: + clr.GetClrType(t) + return True + except TypeError: + return False + _prop_types = [type(property())] try: _prop_types.append(types.GetSetDescriptorType) except: pass @@ -758,6 +767,27 @@ class ModuleRedeclarator(object): spec.append("**" + kwarg) return flatten(spec) + def restoreClr(self, p_func, p_name, p_class): + """Restore the function signature by the CLR type signature""" + clr_type = clr.GetClrType(p_class) + if p_name == '__new__': + methods = clr_type.GetConstructors() + if not methods: + return p_name + '(*args) # could not find CLR constructor' + else: + methods = [m for m in clr_type.GetMethods() if m.Name == p_name] + if not methods: + return p_name + '(*args) # could not find CLR method' + method = methods[0] + for overload in methods[1:]: + if len(overload.GetParameters()) < len(method.GetParameters()): method = overload + params = [p.Name for p in method.GetParameters()] + if not method.IsStatic: + params = ['self'] + params + if len(methods) > 1: + params.append("*___args") + return p_name + '(' + ', '.join(params) + ')' + def redoFunction(self, p_func, p_name, indent, p_class=None, p_modname=None): """ Restore function argument list as best we can. @@ -790,6 +820,7 @@ class ModuleRedeclarator(object): #self.out("@staticmethod # known case of __new__", indent) deco = "staticmethod" deco_comment = " # known case of __new__" + if deco and HAS_DECORATORS: self.out("@" + deco + deco_comment, indent) if inspect and inspect.isfunction(p_func): @@ -799,7 +830,10 @@ class ModuleRedeclarator(object): spec, sig_note = self.restorePredefinedBuiltin(classname, p_name) self.out("def " + spec + ": # " + sig_note, indent) self.outDocAttr(p_func, indent+1, p_class) - + elif sys.platform == 'cli' and is_clr_type(p_class): + spec = self.restoreClr(p_func, p_name, p_class) + self.out("def " + spec + ":", indent) + self.outDocAttr(p_func, indent+1, p_class) else: # __doc__ is our best source of arglist sig_note = "real signature unknown" @@ -1088,13 +1122,16 @@ if __name__ == "__main__": else: doing_builtins = False - refs = opts.get('-c', '') - if refs: - import clr - for ref in refs.split(';'): clr.AddReferenceByPartialName(ref) + if sys.platform == 'cli': + refs = opts.get('-c', '') + if refs: + for ref in refs.split(';'): clr.AddReferenceByPartialName(ref) - if '-p' in opts: - atexit.register(print_profile) + if '-p' in opts: + atexit.register(print_profile) + + from System import DateTime + start = DateTime.Now # go on for name in names: @@ -1156,3 +1193,6 @@ if __name__ == "__main__": raise else: continue + + if sys.platform == 'cli': + print "Generation completed in " + str((DateTime.Now - start).TotalMilliseconds) + " ms" From 7cf9883735bb789e5cdebfeb516465a091b8fa91 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 6 Apr 2010 21:40:15 +0400 Subject: [PATCH 13/17] initial version of quickfix to trigger generation of IronPython binary stubs --- .../actions/GenerateBinaryStubsFix.java | 111 ++++++++++++++++++ .../PyUnresolvedReferencesInspection.java | 8 +- .../python/psi/PyCallExpression.java | 7 ++ .../python/psi/impl/PyCallExpressionImpl.java | 8 ++ .../python/psi/impl/PyDecoratorImpl.java | 8 ++ .../jetbrains/python/sdk/PythonSdkType.java | 65 +++++----- .../python/sdk/SkeletonVersionUpdater.java | 7 +- 7 files changed, 178 insertions(+), 36 deletions(-) create mode 100644 python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java diff --git a/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java b/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java new file mode 100644 index 000000000000..a95bc63073a5 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/GenerateBinaryStubsFix.java @@ -0,0 +1,111 @@ +package com.jetbrains.python.actions; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyImportReferenceImpl; +import com.jetbrains.python.sdk.IronPythonSdkFlavor; +import com.jetbrains.python.sdk.PythonSdkFlavor; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author yole + */ +public class GenerateBinaryStubsFix implements LocalQuickFix { + private String myQualifiedName; + private Sdk mySdk; + + public GenerateBinaryStubsFix(PsiReference reference) { + myQualifiedName = getReferenceText(reference); + mySdk = getPythonSdk(reference); + } + + private static String getReferenceText(PsiReference reference) { + PsiElement element = reference.getElement(); + while (element.getParent() instanceof PyReferenceExpression) { + element = element.getParent(); + } + return element.getText(); + } + + @NotNull + public String getName() { + return "Generate stubs for binary module " + myQualifiedName; + } + + @NotNull + public String getFamilyName() { + return "GenerateBinaryStubs"; + } + + public void applyFix(@NotNull Project project, @NotNull final ProblemDescriptor descriptor) { + ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + List assemblyRefs = collectAssemblyReferences(descriptor.getPsiElement().getContainingFile()); + final String skeletonPath = PythonSdkType.findSkeletonsPath(mySdk); + PythonSdkType.generateSkeleton(mySdk.getHomePath(), skeletonPath, myQualifiedName, assemblyRefs); + final VirtualFile skeletonDir = LocalFileSystem.getInstance().findFileByPath(skeletonPath); + if (skeletonDir != null) { + skeletonDir.refresh(true, true); + } + } + }, "Generating stubs for binary module", false, project); + } + + private List collectAssemblyReferences(PsiFile file) { + if (!(PythonSdkFlavor.getFlavor(mySdk.getHomePath()) instanceof IronPythonSdkFlavor)) { + return Collections.emptyList(); + } + final List result = new ArrayList(); + file.accept(new PyRecursiveElementVisitor() { + @Override + public void visitPyCallExpression(PyCallExpression node) { + super.visitPyCallExpression(node); + if (node.isCalleeText("AddReference") || node.isCalleeText("AddReferenceByPartialName")) { + final PyExpression[] args = node.getArguments(); + if (args.length == 1 && args [0] instanceof PyStringLiteralExpression) { + result.add(((PyStringLiteralExpression) args [0]).getStringValue()); + } + } + } + }); + return result; + } + + public static boolean isApplicable(PsiReference ref) { + if (!(ref instanceof PyImportReferenceImpl)) { + return false; + } + final Sdk sdk = getPythonSdk(ref); + if (sdk == null) { + return false; + } + final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk.getHomePath()); + if (flavor instanceof IronPythonSdkFlavor) { + return getReferenceText(ref).contains("System"); + } + return false; + } + + @Nullable + private static Sdk getPythonSdk(PsiReference ref) { + final Module module = ModuleUtil.findModuleForPsiElement(ref.getElement()); + return module == null ? null : PythonSdkType.findPythonSdk(module); + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index a6fdd3f44a44..758a9fe9f674 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -16,10 +16,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.Consumer; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; -import com.jetbrains.python.actions.AddFieldQuickFix; -import com.jetbrains.python.actions.AddImportAction; -import com.jetbrains.python.actions.AddMethodQuickFix; -import com.jetbrains.python.actions.ImportFromExistingFix; +import com.jetbrains.python.actions.*; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; @@ -373,6 +370,9 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool { hl_type = ProblemHighlightType.LIKE_UNKNOWN_SYMBOL; } + if (GenerateBinaryStubsFix.isApplicable(reference)) { + actions.add(new GenerateBinaryStubsFix(reference)); + } addPluginQuickFixes(reference, actions); PsiElement point = node.getLastChild(); // usually the identifier at the end of qual ref diff --git a/python/src/com/jetbrains/python/psi/PyCallExpression.java b/python/src/com/jetbrains/python/psi/PyCallExpression.java index bad20f2d0cec..2ed97eba8c30 100644 --- a/python/src/com/jetbrains/python/psi/PyCallExpression.java +++ b/python/src/com/jetbrains/python/psi/PyCallExpression.java @@ -38,6 +38,13 @@ public interface PyCallExpression extends PyExpression { @Nullable PyMarkedFunction resolveCallee(); + /** + * Checks if the unqualified name of the callee matches the specified text. + * + * @param name the text to check + * @return true if matches, false otherwise + */ + boolean isCalleeText(@NotNull String name); /** * Couples function with a flag describing the way it is called. diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java index 8ffc1d701f88..fbd9f7ea78e6 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java @@ -49,6 +49,14 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress return PyCallExpressionHelper.resolveCallee(this); } + public boolean isCalleeText(@NotNull String name) { + final PyExpression callee = getCallee(); + if (!(callee instanceof PyReferenceExpression)) { + return false; + } + return name.equals(((PyReferenceExpression)callee).getReferencedName()); + } + @Override public String toString() { return "PyCallExpression: " + PyUtil.getReadableRepr(getCallee(), true); //or: getCalledFunctionReference().getReferencedName(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java index c38540518c7f..1e9e657ad43c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyDecoratorImpl.java @@ -102,6 +102,14 @@ public class PyDecoratorImpl extends PyPresentableElementImpl i return callee; } + public boolean isCalleeText(@NotNull String name) { + final PyExpression callee = getCallee(); + if (!(callee instanceof PyReferenceExpression)) { + return false; + } + return name.equals(((PyReferenceExpression)callee).getReferencedName()); + } + @Override public String toString() { return "PyDecorator: @" + PyUtil.getReadableRepr(getCallee(), true); //getCalledFunctionReference().getReferencedName(); diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index e83e914a47e9..588f3bb3b6cc 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -48,10 +48,7 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.TreeSet; +import java.util.*; import static com.jetbrains.python.psi.PyUtil.sure; @@ -98,8 +95,6 @@ public class PythonSdkType extends SdkType { return PyBuiltinCache.BUILTIN_FILE; } - @NonNls static final private String PYTHON_STR = "python"; - @NonNls @Nullable public String suggestHomePath() { @@ -352,9 +347,8 @@ public class PythonSdkType extends SdkType { } } // fix skeletons as needed - String url = findSkeletonsUrl(currentSdk); - if (url != null) { - final String path = VfsUtil.urlToPath(url); + final String path = findSkeletonsPath(currentSdk); + if (path != null) { File stubs_dir = new File(path); if (!stubs_dir.exists()) { final ProgressManager progman = ProgressManager.getInstance(); @@ -364,7 +358,7 @@ public class PythonSdkType extends SdkType { public void run(@NotNull final ProgressIndicator indicator) { try { generateBuiltinStubs(currentSdk.getHomePath(), path); - generateBinaryStubs(currentSdk.getHomePath(), path, indicator); + generateBinarySkeletons(currentSdk.getHomePath(), path, indicator); } catch (Exception e) { LOG.error(e); @@ -410,11 +404,11 @@ public class PythonSdkType extends SdkType { } @Nullable - public static String findSkeletonsUrl(Sdk sdk) { + public static String findSkeletonsPath(Sdk sdk) { final String[] urls = sdk.getRootProvider().getUrls(BUILTIN_ROOT_TYPE); for (String url : urls) { if (url.contains(SKELETON_DIR_NAME)) { - return url; + return VfsUtil.urlToPath(url); } } return null; @@ -528,7 +522,7 @@ public class PythonSdkType extends SdkType { // regenerate stubs, existing or not final File stubs_dir = new File(stubs_path); if (!stubs_dir.exists()) stubs_dir.mkdirs(); - generateBinaryStubs(bin_path, stubs_path, indicator); + generateBinarySkeletons(bin_path, stubs_path, indicator); } } @@ -603,8 +597,7 @@ public class PythonSdkType extends SdkType { * @param stubsRoot where to put results (expected to exist). * @param indicator ProgressIndicator to update, or null. */ - public static void generateBinaryStubs(final String binaryPath, final String stubsRoot, ProgressIndicator indicator) - { + public static void generateBinarySkeletons(final String binaryPath, final String stubsRoot, ProgressIndicator indicator) { if (indicator != null) { indicator.setText("Generating skeletons of binary libs"); } @@ -635,19 +628,7 @@ public class PythonSdkType extends SdkType { indicator.setText2(modname); } LOG.info("Skeleton for " + modname); - final ProcessOutput gen_result = SdkUtil.getProcessOutput( - parent_dir, - new String[]{binaryPath, PythonHelpersLocator.getHelperPath(GENERATOR3), "-d", stubsRoot, modname}, - getVirtualEnvAdditionalEnv(binaryPath), - RUN_TIMEOUT - ); - if (gen_result.getExitCode() != 0) { - StringBuffer sb = new StringBuffer("Skeleton for "); - sb.append(modname).append(" failed. stderr: --"); - for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n"); - sb.append("--"); - LOG.warn(sb.toString()); - } + generateSkeleton(binaryPath, stubsRoot, modname, Collections.emptyList()); } } } @@ -661,6 +642,34 @@ public class PythonSdkType extends SdkType { } } + public static void generateSkeleton(String binaryPath, String stubsRoot, String modname, List assemblyRefs) { + final String parent_dir = new File(binaryPath).getParent(); + List commandLine = new ArrayList(); + commandLine.add(binaryPath); + commandLine.add(PythonHelpersLocator.getHelperPath(GENERATOR3)); + commandLine.add("-d"); + commandLine.add(stubsRoot); + if (!assemblyRefs.isEmpty()) { + commandLine.add("-c"); + commandLine.add(StringUtil.join(assemblyRefs, ";")); + } + commandLine.add(modname); + + final ProcessOutput gen_result = SdkUtil.getProcessOutput( + parent_dir, + commandLine.toArray(new String[commandLine.size()]), + getVirtualEnvAdditionalEnv(binaryPath), + RUN_TIMEOUT*10 + ); + if (gen_result.getExitCode() != 0) { + StringBuffer sb = new StringBuffer("Skeleton for "); + sb.append(modname).append(" failed. stderr: --"); + for (String err_line : gen_result.getStderrLines()) sb.append(err_line).append("\n"); + sb.append("--"); + LOG.warn(sb.toString()); + } + } + public static List getAllSdks() { return ProjectJdkTable.getInstance().getSdksOfType(getInstance()); } diff --git a/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java b/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java index 1e7f31cd9795..7cea7f88da2b 100644 --- a/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java +++ b/python/src/com/jetbrains/python/sdk/SkeletonVersionUpdater.java @@ -6,7 +6,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.LocalFileSystem; import org.jetbrains.annotations.NotNull; import java.io.*; @@ -30,10 +30,9 @@ public class SkeletonVersionUpdater implements ProjectComponent { writeVersion(versionFile, SKELETONS_VERSION); final List sdkList = PythonSdkType.getAllSdks(); for (Sdk sdk : sdkList) { - final String url = PythonSdkType.findSkeletonsUrl(sdk); - final String path = VfsUtil.urlToPath(url); + final String path = PythonSdkType.findSkeletonsPath(sdk); PythonSdkType.generateBuiltinStubs(sdk.getHomePath(), path); - PythonSdkType.generateBinaryStubs(sdk.getHomePath(), path, ProgressManager.getInstance().getProgressIndicator()); + PythonSdkType.generateBinarySkeletons(sdk.getHomePath(), path, ProgressManager.getInstance().getProgressIndicator()); } } } From a1cfd7bc38f006ec45d6477b17dc0c415cbd0f92 Mon Sep 17 00:00:00 2001 From: "alexey.ivanov" Date: Wed, 7 Apr 2010 15:28:41 +0400 Subject: [PATCH 14/17] test fix --- .../python/actions/TransformClassicClassQuickFix.java | 5 +++-- python/testData/inspections/TransformClassicClass.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/actions/TransformClassicClassQuickFix.java b/python/src/com/jetbrains/python/actions/TransformClassicClassQuickFix.java index 68251120da4f..7a5a45ccad66 100644 --- a/python/src/com/jetbrains/python/actions/TransformClassicClassQuickFix.java +++ b/python/src/com/jetbrains/python/actions/TransformClassicClassQuickFix.java @@ -4,9 +4,9 @@ import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; -import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyExpression; @@ -31,7 +31,8 @@ public class TransformClassicClassQuickFix implements LocalQuickFix { public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement psiElement = descriptor.getPsiElement(); - if (psiElement instanceof PyClass) { + psiElement = PsiTreeUtil.getParentOfType(psiElement, PyClass.class); + if (psiElement != null) { PyClass pyClass = (PyClass) psiElement; PyExpression[] superClassExpressions = pyClass.getSuperClassExpressions(); PyElementGenerator generator = PyElementGenerator.getInstance(project); diff --git a/python/testData/inspections/TransformClassicClass.py b/python/testData/inspections/TransformClassicClass.py index 492d098bcda8..5548b114394a 100644 --- a/python/testData/inspections/TransformClassicClass.py +++ b/python/testData/inspections/TransformClassicClass.py @@ -1,6 +1,6 @@ -class A: +class A: def foo(self): pass def bar(self): - pass \ No newline at end of file + pass \ No newline at end of file From d502b27c8b39341ab54b19dadad6415e9cf37ee4 Mon Sep 17 00:00:00 2001 From: "alexey.ivanov" Date: Wed, 7 Apr 2010 16:35:02 +0400 Subject: [PATCH 15/17] PY-812 Octal literal inspection erroneously triggers on hex literals --- .../jetbrains/python/validation/UnsupportedFeatures.java | 9 ++++++--- .../highlighting/unsupportedFeaturesInPython3.py | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/validation/UnsupportedFeatures.java b/python/src/com/jetbrains/python/validation/UnsupportedFeatures.java index 41cd296a5565..73aee2f5342a 100644 --- a/python/src/com/jetbrains/python/validation/UnsupportedFeatures.java +++ b/python/src/com/jetbrains/python/validation/UnsupportedFeatures.java @@ -161,9 +161,12 @@ public class UnsupportedFeatures extends PyAnnotator { getHolder().createWarningAnnotation(node, "Integer literals do not support a trailing \'l\' or \'L\' in Python 3").registerFix(new RemoveTrailingLIntention()); } - if (text.length() > 1 && text.charAt(0) == '0' && (text.charAt(1) != 'o' || text.charAt(1) != 'b')) { - getHolder().createWarningAnnotation(node, - "Python 3 requires '0o' prefix for octal literals").registerFix(new ReplaceOctalNumericLiteralIntention()); + if (text.length() > 1 && text.charAt(0) == '0') { + char c = Character.toLowerCase(text.charAt(1)); + if (c != 'o' && c != 'b' && c != 'x') { + getHolder().createWarningAnnotation(node, + "Python 3 requires '0o' prefix for octal literals").registerFix(new ReplaceOctalNumericLiteralIntention()); + } } } } diff --git a/python/testData/highlighting/unsupportedFeaturesInPython3.py b/python/testData/highlighting/unsupportedFeaturesInPython3.py index ee51aecc611e..5da23e01965c 100644 --- a/python/testData/highlighting/unsupportedFeaturesInPython3.py +++ b/python/testData/highlighting/unsupportedFeaturesInPython3.py @@ -3,6 +3,8 @@ print(a <> 3raw_input() a = 123l a = 043 +a = 0X43 +a = 0b1 s = u"text" raise a, b, c raise a, b From 7572c7afd3c92b4e11cc20d4954a8c6ca0b71ef4 Mon Sep 17 00:00:00 2001 From: Oleg Shpynov Date: Wed, 7 Apr 2010 19:07:31 +0400 Subject: [PATCH 16/17] Initial version of Python XMLRPC based REPL console --- python/helpers/pydev/pydevconsole.py | 225 ++++++++++++++++++ python/python.iml | 1 + python/src/META-INF/python-plugin-common.xml | 5 + .../console/PyConsoleProcessHandler.java | 31 ++- .../python/console/PyConsoleRunner.java | 79 +++--- .../python/console/PydevConsoleRunner.java | 123 ++++++++++ .../console/RunPythonConsoleActionNew.java | 64 +++++ 7 files changed, 469 insertions(+), 59 deletions(-) create mode 100644 python/helpers/pydev/pydevconsole.py create mode 100644 python/src/com/jetbrains/python/console/PydevConsoleRunner.java create mode 100644 python/src/com/jetbrains/python/console/RunPythonConsoleActionNew.java diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py new file mode 100644 index 000000000000..0e72d63d024d --- /dev/null +++ b/python/helpers/pydev/pydevconsole.py @@ -0,0 +1,225 @@ +try: + from code import InteractiveConsole +except ImportError: + from pydevconsole_code_for_ironpython import InteractiveConsole + +import os +import sys + +try: + False + True +except NameError: # version < 2.3 -- didn't have the True/False builtins + import __builtin__ + setattr(__builtin__, 'True', 1) #Python 3.0 does not accept __builtin__.True = 1 in its syntax + setattr(__builtin__, 'False', 0) + +try: + try: + import xmlrpclib + except ImportError: + import xmlrpc.client as xmlrpclib +except ImportError: + import _pydev_xmlrpclib as xmlrpclib + +#======================================================================================================================= +# StdIn +#======================================================================================================================= +class StdIn: + ''' + Object to be added to stdin (to emulate it as non-blocking while the next line arrives) + ''' + + def __init__(self, interpreter, host, client_port): + self.interpreter = interpreter + self.client_port = client_port + self.host = host + + def readline(self, *args, **kwargs): #@UnusedVariable + #Ok, callback into the client to see get the new input + server = xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port)) + return server.RequestInput() + + def isatty(self): + return False #not really a file + + def write(self, *args, **kwargs): + pass #not available StdIn (but it can be expected to be in the stream interface) + + def flush(self, *args, **kwargs): + pass #not available StdIn (but it can be expected to be in the stream interface) + + #in the interactive interpreter, a read and a readline are the same. + read = readline + +#======================================================================================================================= +# InterpreterInterface +#======================================================================================================================= +class InterpreterInterface: + ''' + The methods in this class should be registered in the xml-rpc server. + ''' + + def __init__(self, host, client_port): + self.client_port = client_port + self.host = host + self.namespace = {} + self.interpreter = InteractiveConsole(self.namespace) + + + def addExec(self, line): + #f_opened = open('c:/temp/a.txt', 'a') + #f_opened.write(line+'\n') + + original_in = sys.stdin + try: + help = None + if 'pydoc' in sys.modules: + pydoc = sys.modules['pydoc'] #Don't import it if it still is not there. + + + if hasattr(pydoc, 'help'): + #You never know how will the API be changed, so, let's code defensively here + help = pydoc.help + if not hasattr(help, 'input'): + help = None + except: + #Just ignore any error here + pass + + sys.stdin = StdIn(self, self.host, self.client_port) + if help is not None: + #This will enable the help() function to work. + help.input = sys.stdin + + try: + more = self.interpreter.push(line) + finally: + if help is not None: + help.input = original_in + + sys.stdin = original_in + + #it's always false at this point + need_input = False + return more, need_input + + + def getCompletions(self, text): + from _completer import Completer + completer = Completer(self.namespace, None) + return completer.complete(text) + + + def getDescription(self, text): + obj = None + if '.' not in text: + try: + obj = self.namespace[text] + except KeyError: + return '' + + else: + try: + splitted = text.split('.') + obj = self.namespace[splitted[0]] + for t in splitted[1:]: + obj = getattr(obj, t) + except: + return '' + + + if obj is not None: + try: + if sys.platform.startswith("java"): + #Jython + doc = obj.__doc__ + if doc is not None: + return doc + + import jyimportsTipper + is_method, infos = jyimportsTipper.ismethod(obj) + ret = '' + if is_method: + for info in infos: + ret += info.getAsDoc() + return ret + + else: + #Python and Iron Python + import inspect #@UnresolvedImport + doc = inspect.getdoc(obj) + if doc is not None: + return doc + except: + pass + + try: + #if no attempt succeeded, try to return repr()... + return repr(obj) + except: + try: + #otherwise the class + return str(obj.__class__) + except: + #if all fails, go to an empty string + return '' + + + def close(self): + sys.exit(0) + + + +#======================================================================================================================= +# _DoExit +#======================================================================================================================= +def _DoExit(*args): + ''' + We have to override the exit because calling sys.exit will only actually exit the main thread, + and as we're in a Xml-rpc server, that won't work. + ''' + + try: + import java.lang.System + java.lang.System.exit(1) + except ImportError: + if len(args) == 1: + os._exit(args[0]) + else: + os._exit(0) + +#======================================================================================================================= +# StartServer +#======================================================================================================================= +def StartServer(host, port, client_port): + #replace exit (see comments on method) + #note that this does not work in jython!!! (sys method can't be replaced). + sys.exit = _DoExit + + try: + try: + from SimpleXMLRPCServer import SimpleXMLRPCServer + except ImportError: + from xmlrpc.server import SimpleXMLRPCServer + except ImportError: + from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer + + interpreter = InterpreterInterface(host, client_port) + server = SimpleXMLRPCServer((host, port), logRequests=False) + + server.register_function(interpreter.addExec) + server.register_function(interpreter.getCompletions) + server.register_function(interpreter.getDescription) + server.register_function(interpreter.close) + + server.serve_forever() + + +#======================================================================================================================= +# main +#======================================================================================================================= +if __name__ == '__main__': + port, client_port = sys.argv[1:3] + StartServer('localhost', int(port), int(client_port)) + diff --git a/python/python.iml b/python/python.iml index 2eed16a30183..85eba30f20b6 100644 --- a/python/python.iml +++ b/python/python.iml @@ -37,6 +37,7 @@ + diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index ace147507f09..198e8a08a20e 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -316,6 +316,11 @@ text="Run Python Console..." description="Allows to quickly run Python console"> + + + diff --git a/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java b/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java index 9dc63061fe4f..087ffbfb3812 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java +++ b/python/src/com/jetbrains/python/console/PyConsoleProcessHandler.java @@ -4,6 +4,7 @@ import com.intellij.execution.console.LanguageConsoleImpl; import com.intellij.execution.process.ColoredProcessHandler; import com.intellij.execution.process.ConsoleHighlighter; import com.intellij.execution.process.OSProcessHandler; +import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -19,16 +20,16 @@ import java.util.regex.Pattern; /** * @author oleg */ -class PyConsoleProcessHandler extends OSProcessHandler { - private final PyConsoleRunner myPyConsoleRunner; +public class PyConsoleProcessHandler extends OSProcessHandler { private final Charset myCharset; + private final LanguageConsoleImpl myLanguageConsole; - public PyConsoleProcessHandler(final PyConsoleRunner pyConsoleRunner, - final Process process, + public PyConsoleProcessHandler(final Process process, + final LanguageConsoleImpl languageConsole, final String commandLine, final Charset charset) { super(process, commandLine); - myPyConsoleRunner = pyConsoleRunner; + myLanguageConsole = languageConsole; myCharset = charset; } @@ -60,27 +61,31 @@ class PyConsoleProcessHandler extends OSProcessHandler { @Override public void notifyTextAvailable(final String text, final Key attributes) { - final LanguageConsoleImpl languageConsole = myPyConsoleRunner.getLanguageConsole(); - String string = processPrompts(languageConsole, StringUtil.convertLineSeparators(text)); + String string = processPrompts(myLanguageConsole, StringUtil.convertLineSeparators(text)); + processOutput(myLanguageConsole, string, attributes); + } + public static void processOutput(LanguageConsoleImpl console, String string, final Key attributes) { + final ConsoleViewContentType type = + attributes == ProcessOutputTypes.STDERR ? ConsoleViewContentType.ERROR_OUTPUT : ConsoleViewContentType.NORMAL_OUTPUT; // Highlight output by pattern Matcher matcher; while ((matcher = CODE_ELEMENT_PATTERN.matcher(string)).find()) { - printToConsole(languageConsole, string.substring(0, matcher.start()), ConsoleViewContentType.NORMAL_OUTPUT); + printToConsole(console, string.substring(0, matcher.start()), type); // Number group if (matcher.group(1) != null) { - printToConsole(languageConsole, matcher.group(1), NUMBER_ATTRIBUTES); + printToConsole(console, matcher.group(1), NUMBER_ATTRIBUTES); } // String group else if (matcher.group(6) != null) { - printToConsole(languageConsole, matcher.group(6), STRING_ATTRIBUTES); + printToConsole(console, matcher.group(6), STRING_ATTRIBUTES); } else { - printToConsole(languageConsole, matcher.group(), ConsoleViewContentType.NORMAL_OUTPUT); + printToConsole(console, matcher.group(), type); } string = string.substring(matcher.end()); } - printToConsole(languageConsole, string, ConsoleViewContentType.NORMAL_OUTPUT); + printToConsole(console, string, type); } private String processPrompts(final LanguageConsoleImpl languageConsole, String string) { @@ -119,7 +124,7 @@ class PyConsoleProcessHandler extends OSProcessHandler { final TextAttributes attributes = TextAttributes.merge(type.getAttributes(), ConsoleHighlighter.OUT.getDefaultAttributes()); ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { - console.addToHistory(string, attributes); + console.printToHistory(string, attributes); } }, ModalityState.stateForComponent(console.getComponent())); } diff --git a/python/src/com/jetbrains/python/console/PyConsoleRunner.java b/python/src/com/jetbrains/python/console/PyConsoleRunner.java index 2995a323f530..62f897df9fbd 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PyConsoleRunner.java @@ -7,16 +7,11 @@ import com.intellij.execution.ExecutionManager; import com.intellij.execution.Executor; import com.intellij.execution.ExecutorRegistry; import com.intellij.execution.console.LanguageConsoleImpl; -import com.intellij.execution.console.LanguageConsoleViewImpl; -import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.executors.DefaultRunExecutor; -import com.intellij.execution.filters.Filter; import com.intellij.execution.process.*; import com.intellij.execution.ui.RunContentDescriptor; -import com.intellij.execution.ui.RunContentManager; import com.intellij.execution.ui.actions.CloseAction; import com.intellij.ide.CommonActionsManager; -import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; @@ -38,16 +33,10 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiFile; -import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentFactory; import com.intellij.util.PairProcessor; import com.jetbrains.django.run.ExecutionHelper; import com.jetbrains.django.run.Runner; -import com.jetbrains.django.util.DjangoUtil; -import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyUtil; -import com.jetbrains.python.psi.types.PyModuleType; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -67,17 +56,17 @@ public class PyConsoleRunner { private final String myConsoleTitle; private OSProcessHandler myProcessHandler; - private final CommandLineArgumentsProvider myProvider; - private final String myWorkingDir; + protected final CommandLineArgumentsProvider myProvider; + protected final String myWorkingDir; - private PyLanguageConsoleView myConsoleView; + protected PyLanguageConsoleView myConsoleView; private final ConsoleHistoryModel myHistory = new ConsoleHistoryModel(); private AnAction myRunAction; - private PyConsoleRunner(@NotNull final Project project, + public PyConsoleRunner(@NotNull final Project project, @NotNull final String consoleTitle, @NotNull final CommandLineArgumentsProvider provider, - @Nullable final String workingDir) throws ExecutionException { + @Nullable final String workingDir) { myProject = project; myConsoleTitle = consoleTitle; myProvider = provider; @@ -88,40 +77,27 @@ public class PyConsoleRunner { @NotNull final String consoleTitle, @NotNull final CommandLineArgumentsProvider provider, @Nullable final String workingDir) { + + final PyConsoleRunner consoleRunner = new PyConsoleRunner(project, consoleTitle, provider, workingDir); try { - final PyConsoleRunner consoleRunner = createRunner(project, consoleTitle, provider, workingDir); - initAndRun(consoleRunner); + consoleRunner.initAndRun(); } catch (ExecutionException e) { ExecutionHelper.showErrors(project, Arrays.asList(e), consoleTitle, null); } } - @NotNull - public static PyConsoleRunner createRunner(@NotNull final Project project, - @NotNull final String consoleTitle, - @NotNull final CommandLineArgumentsProvider provider, - @Nullable final String workingDir) throws ExecutionException { - return new PyConsoleRunner(project, consoleTitle, provider, workingDir); - } - - public static void initAndRun(@NotNull final PyConsoleRunner runner) throws ExecutionException { - runner.init(); - runner.doRun(); - } - - private void init() throws ExecutionException { -// add holder created - final Process process = Runner.createProcess(myWorkingDir, true, myProvider.getAdditionalEnvs(), myProvider.getArguments()); - - final Charset outputEncoding = EncodingManager.getInstance().getDefaultCharset(); - myProcessHandler = new PyConsoleProcessHandler(this, process, getProviderCommandLine(), outputEncoding); - - ProcessTerminatedListener.attach(myProcessHandler); + public void initAndRun() throws ExecutionException { + // Create Server process + final Process process = createProcess(); // Init console view myConsoleView = new PyLanguageConsoleView(myProject, myConsoleTitle); + myProcessHandler = createProcessHandler(process); + + ProcessTerminatedListener.attach(myProcessHandler); + // Set language level for (Module module : ModuleManager.getInstance(myProject).getModules()) { final Sdk pythonSdk = PythonSdkType.findPythonSdk(module); @@ -138,7 +114,7 @@ public class PyConsoleRunner { break; } } - + myProcessHandler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(ProcessEvent event) { @@ -191,6 +167,19 @@ public class PyConsoleRunner { IdeFocusManager.getInstance(myProject).requestFocus(getLanguageConsole().getCurrentEditor().getContentComponent(), true); } }); +// Run + myProcessHandler.startNotify(); + } + + + @Nullable + protected Process createProcess() throws ExecutionException { + return Runner.createProcess(myWorkingDir, true, myProvider.getAdditionalEnvs(), myProvider.getArguments()); + } + + protected PyConsoleProcessHandler createProcessHandler(final Process process) { + final Charset outputEncoding = EncodingManager.getInstance().getDefaultCharset(); + return new PyConsoleProcessHandler(process, myConsoleView.getConsole(), getProviderCommandLine(), outputEncoding); } private void registerActionShortcuts(final AnAction[] actions, final JComponent component) { @@ -252,11 +241,9 @@ public class PyConsoleRunner { return new AnAction[]{stopAction, closeAction, myRunAction, historyNextAction, historyPrevAction}; } - private void doRun() { - myProcessHandler.startNotify(); - } - - private static void sendInput(final String input, final Charset charset, final OutputStream outputStream) { + protected void sendInput(final String input) { + final Charset charset = myProcessHandler.getCharset(); + final OutputStream outputStream = myProcessHandler.getProcessInput(); try { byte[] bytes = input.getBytes(charset.name()); outputStream.write(bytes); @@ -285,7 +272,7 @@ public class PyConsoleRunner { myHistory.addToHistory(line); } final String text2send = line.length() == 0 ? "\n\n" : line + "\n"; - sendInput(text2send, myProcessHandler.getCharset(), myProcessHandler.getProcessInput()); + sendInput(text2send); myConsoleView.inputSent(text2send); } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java new file mode 100644 index 000000000000..3494a35172f0 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -0,0 +1,123 @@ +package com.jetbrains.python.console; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.console.LanguageConsoleImpl; +import com.intellij.execution.process.*; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.encoding.EncodingManager; +import com.intellij.util.net.NetUtils; +import com.jetbrains.django.run.ExecutionHelper; +import com.jetbrains.django.run.Runner; +import com.jetbrains.django.util.DjangoUtil; +import com.jetbrains.python.PythonHelpersLocator; +import com.jetbrains.python.console.pydev.ICallback; +import com.jetbrains.python.console.pydev.InterpreterResponse; +import com.jetbrains.python.console.pydev.PydevConsoleCommunication; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +/** + * @author oleg + */ +public class PydevConsoleRunner extends PyConsoleRunner { + private final int[] myPorts; + private PydevConsoleCommunication myPydevConsoleCommunication; + + protected PydevConsoleRunner(@NotNull final Project project, + @NotNull final String consoleTitle, + @NotNull final CommandLineArgumentsProvider provider, + @Nullable final String workingDir, + int[] ports) { + super(project, consoleTitle, provider, workingDir); + myPorts = ports; + } + + public static void run(@NotNull final Project project, + @NotNull final Module module, + @NotNull final Sdk sdk) { + final String consoleTitle = "TRUE console"; + final int[] ports; + try { + // File "pydev/pydevconsole.py", line 223, in + // port, client_port = sys.argv[1:3] + ports = NetUtils.findAvailableSocketPorts(2); + } + catch (IOException e) { + ExecutionHelper.showErrors(project, Arrays.asList(e), consoleTitle, null); + return; + } + final ArrayList args = new ArrayList(Arrays.asList(sdk.getHomePath(), "-u", PythonHelpersLocator.getHelperPath("pydev/pydevconsole.py"))); + for (int port : ports) { + args.add(String.valueOf(port)); + } + final CommandLineArgumentsProvider provider = new CommandLineArgumentsProvider() { + public String[] getArguments() { + return args.toArray(new String[args.size()]); + } + + public boolean passParentEnvs() { + return false; + } + + public Map getAdditionalEnvs() { + return Collections.emptyMap(); + } + }; + + final PydevConsoleRunner consoleRunner = new PydevConsoleRunner(project, consoleTitle, provider, DjangoUtil.getProjectRoot(module), ports); + try { + consoleRunner.initAndRun(); + } + catch (ExecutionException e) { + ExecutionHelper.showErrors(project, Arrays.asList(e), consoleTitle, null); + } + } + + @Override + protected Process createProcess() throws ExecutionException { + final Process server = Runner.createProcess(myWorkingDir, true, myProvider.getAdditionalEnvs(), myProvider.getArguments()); + try { + myPydevConsoleCommunication = new PydevConsoleCommunication(getProject(), myPorts[0], server, myPorts[1]); + } + catch (Exception e) { + throw new ExecutionException(e.getMessage()); + } + return server; + } + + @Override + public void initAndRun() throws ExecutionException { + super.initAndRun(); + sendInput("import sys; print('Python %s on %s' % (sys.version, sys.platform))\n"); + } + + @Override + protected void sendInput(final String input) { + if (myPydevConsoleCommunication != null){ + myPydevConsoleCommunication.execInterpreter(input, new ICallback() { + public Object call(final InterpreterResponse interpreterResponse) { + final LanguageConsoleImpl console = myConsoleView.getConsole(); + // TODO[oleg] More sophisticated callback required! + // At least this one works! + if (!StringUtil.isEmpty(interpreterResponse.err)){ + PyConsoleProcessHandler.processOutput(console, interpreterResponse.err, ProcessOutputTypes.STDERR); + } else { + PyConsoleProcessHandler.processOutput(console, interpreterResponse.out, ProcessOutputTypes.STDOUT); + } + return null; + } + }); + } + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleActionNew.java b/python/src/com/jetbrains/python/console/RunPythonConsoleActionNew.java new file mode 100644 index 000000000000..3b4d56719d02 --- /dev/null +++ b/python/src/com/jetbrains/python/console/RunPythonConsoleActionNew.java @@ -0,0 +1,64 @@ +package com.jetbrains.python.console; + +import com.intellij.execution.process.CommandLineArgumentsProvider; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.IconLoader; +import com.jetbrains.django.util.DjangoUtil; +import com.jetbrains.python.PythonHelpersLocator; +import com.jetbrains.python.console.PydevConsoleRunner; +import com.jetbrains.python.sdk.PythonSdkType; + +import java.util.Collections; +import java.util.Map; + +/** + * @author oleg + */ +public class RunPythonConsoleActionNew extends AnAction implements DumbAware { + + public RunPythonConsoleActionNew() { + super(); + getTemplatePresentation().setIcon(IconLoader.getIcon("/com/jetbrains/python/python.png")); + } + + @Override + public void update(final AnActionEvent e) { + e.getPresentation().setVisible(false); + e.getPresentation().setEnabled(false); + final Project project = e.getData(LangDataKeys.PROJECT); + if (project != null){ + for (Module module : ModuleManager.getInstance(project).getModules()) { + e.getPresentation().setVisible(true); + if (PythonSdkType.findPythonSdk(module) != null){ + e.getPresentation().setEnabled(true); + break; + } + } + } + } + + public void actionPerformed(final AnActionEvent e) { + final Project project = e.getData(LangDataKeys.PROJECT); + assert project != null : "Project is null"; + Sdk sdk = null; + Module module = null; + for (Module m : ModuleManager.getInstance(project).getModules()) { + module = m; + sdk = PythonSdkType.findPythonSdk(module); + if (sdk != null){ + break; + } + } + assert module != null : "Module is null"; + assert sdk != null : "Sdk is null"; + + PydevConsoleRunner.run(project, module, sdk); + } +} \ No newline at end of file From 3a09ce5eb8418d8b2787564f5e5db25b24b06543 Mon Sep 17 00:00:00 2001 From: "alexey.ivanov" Date: Wed, 7 Apr 2010 19:12:04 +0400 Subject: [PATCH 17/17] Improve PySplitIfIntention --- .../intentions/PySplitIfIntention.java | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PySplitIfIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PySplitIfIntention.java index af9bbcb96a74..3a552ea9294c 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PySplitIfIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PySplitIfIntention.java @@ -9,7 +9,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; @@ -53,27 +52,12 @@ public class PySplitIfIntention extends BaseIntentionAction { } PyIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PyIfStatement.class); PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - StringBuilder builder = new StringBuilder(); - - builder.append("if ").append(element.getLeftExpression().getText()).append(":\n"); - builder.append(" if ").append(element.getRightExpression().getText()).append(":"); - appendStatements(ifStatement.getIfPart().getStatementList(), builder); - final PyIfPart[] elifParts = ifStatement.getElifParts(); - for (PyIfPart elifPart: elifParts) { - builder.append("\n elif ").append(elifPart.getCondition().getText()).append(":"); - appendStatements(elifPart.getStatementList(), builder); - } - final PyElsePart elsePart = ifStatement.getElsePart(); - if (elsePart != null) { - builder.append("\n else:"); - appendStatements(elsePart.getStatementList(), builder); - } - ifStatement.getIfPart().replace(elementGenerator.createFromText(PyIfStatement.class, builder.toString())); - } - - private static void appendStatements(PyStatementList statementList, StringBuilder builder) { - for (PyStatement statement : statementList.getStatements()) { - builder.append("\n ").append(statement.getText()); - } + + PyIfStatement subIf = (PyIfStatement) ifStatement.copy(); + subIf.getIfPart().getCondition().replace(element.getRightExpression()); + ifStatement.getIfPart().getCondition().replace(element.getLeftExpression()); + PyStatementList statementList = elementGenerator.createFromText(PyIfStatement.class, "if a:\n a = 1").getIfPart().getStatementList(); + statementList.getStatements()[0].replace(subIf); + ifStatement.getIfPart().getStatementList().replace(statementList); } }