diff --git a/plugins/org.python.pydev.ast/src/org/python/pydev/ast/interpreter_managers/InterpreterInfo.java b/plugins/org.python.pydev.ast/src/org/python/pydev/ast/interpreter_managers/InterpreterInfo.java index 81e38494a3..7a37bca7bd 100644 --- a/plugins/org.python.pydev.ast/src/org/python/pydev/ast/interpreter_managers/InterpreterInfo.java +++ b/plugins/org.python.pydev.ast/src/org/python/pydev/ast/interpreter_managers/InterpreterInfo.java @@ -65,6 +65,7 @@ import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_core.structure.Tuple; import org.python.pydev.shared_core.utils.PlatformUtils; +import org.python.pydev.shared_core.version.Version; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; @@ -251,6 +252,10 @@ public boolean equals(Object o) { return false; } + if (info.vmArgs.equals(this.vmArgs) == false) { + return false; + } + if (this.pipenvTargetDir != null) { if (info.pipenvTargetDir == null) { return false; @@ -354,6 +359,7 @@ public static InterpreterInfo fromString(String received, boolean askUserInOutPa boolean fromPythonBackend = false; String infoExecutable = null; + String infoVmArgs = null; String infoName = null; String infoVersion = null; String pipenvTargetDir = null; @@ -380,6 +386,9 @@ public static InterpreterInfo fromString(String received, boolean askUserInOutPa } else if ("executable".equals(name)) { infoExecutable = data; + } else if ("vmArgs".equals(name)) { + infoVmArgs = data; + } else if ("pipenv_target_dir".equals(name)) { pipenvTargetDir = data; @@ -495,6 +504,17 @@ public static InterpreterInfo fromString(String received, boolean askUserInOutPa new ArrayList(), forcedLibs, envVars, stringSubstitutionVars); info.setName(infoName); info.setActivateCondaEnv(activateCondaEnv); + if (infoVmArgs != null) { + info.setVmArgs(infoVmArgs); + } else { + if (infoVersion != null) { + Version foundVersion = new Version(infoVersion); + if (foundVersion.isGreaterThanOrEqualTo(new Version("3.11"))) { + info.setVmArgs("-Xfrozen_modules=off"); + } + } + } + info.pipenvTargetDir = pipenvTargetDir; for (String s : predefinedPaths) { info.addPredefinedCompletionsPath(s); @@ -782,6 +802,11 @@ public String toString() { buffer.append("" + activateCondaEnv + "\n"); } + // i.e.: We always save it, even if empty and on load we may set default values. + buffer.append(""); + buffer.append(escape(vmArgs)); + buffer.append("\n"); + if (pipenvTargetDir != null) { buffer.append(""); buffer.append(escape(pipenvTargetDir)); @@ -2057,6 +2082,21 @@ public void removePredefinedCompletionPath(String item) { private boolean activateCondaEnv; + private String vmArgs = ""; + + @Override + public void setVmArgs(String vmArgs) { + if (vmArgs == null) { + throw new NullPointerException("vmArgs may not be null"); + } + this.vmArgs = vmArgs; + } + + @Override + public String getVmArgs() { + return this.vmArgs; + } + private String pipenvTargetDir; public void setLoadFinished(boolean b) { diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterInfo.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterInfo.java index a01c8d75b5..722b594062 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterInfo.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterInfo.java @@ -136,6 +136,10 @@ public UnableToFindExecutableException(String msg) { */ File searchExecutableForInterpreter(String executable, boolean recursive) throws UnableToFindExecutableException; + public String getVmArgs(); + + public void setVmArgs(String vmArgs); + public boolean getActivateCondaEnv(); void setActivateCondaEnv(boolean b); diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterManager.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterManager.java index 1aa85cf5c0..8506d4828e 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterManager.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/IInterpreterManager.java @@ -47,6 +47,11 @@ public interface IInterpreterManager { */ public String IRONPYTHON_INTERNAL_SHELL_VM_ARGS = "IRONPYTHON_INTERNAL_SHELL_VM_ARGS"; + /** + * This is the constant from where we get the default vm args for Python + */ + public String PYTHON_INTERNAL_SHELL_VM_ARGS = "PYTHON_INTERNAL_SHELL_VM_ARGS"; + /** * Constant for the default values */ diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/IPythonNature.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/IPythonNature.java index 429e3b88af..129c8427fc 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/IPythonNature.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/IPythonNature.java @@ -51,6 +51,8 @@ public static class Versions { public static final String JYTHON_PREFIX = "jython"; public static final String IRONYTHON_PREFIX = "ironpython"; + public static final String LATEST_VERSION_NUMBER = "3.12"; + static { ALL_PYTHON_VERSIONS.add(PYTHON_VERSION_3_0); ALL_PYTHON_VERSIONS.add(PYTHON_VERSION_3_6); @@ -169,8 +171,8 @@ public static String convertToInternalVersion(FastStringBuffer buf, final String + " is no longer supported.\n\nPlease refer to: Need to use older Eclipse/Java/Python\n\nin https://www.pydev.org/download.html\n\n"); } // It seems a version we don't directly support, let's check it... - Log.log("Unsupported version:" + version + " (falling back to 3.11)"); - buf.append("3.11"); // latest 3 + Log.log("Unsupported version:" + version + " (falling back to " + LATEST_VERSION_NUMBER + ")"); + buf.append(LATEST_VERSION_NUMBER); // latest 3 String fullVersion = buf.toString(); if (ALL_VERSIONS_ANY_FLAVOR.contains(fullVersion)) { return fullVersion; diff --git a/plugins/org.python.pydev.debug/src/org/python/pydev/debug/ui/launching/PythonRunnerConfig.java b/plugins/org.python.pydev.debug/src/org/python/pydev/debug/ui/launching/PythonRunnerConfig.java index b5908ca6ef..1570dd8856 100644 --- a/plugins/org.python.pydev.debug/src/org/python/pydev/debug/ui/launching/PythonRunnerConfig.java +++ b/plugins/org.python.pydev.debug/src/org/python/pydev/debug/ui/launching/PythonRunnerConfig.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -95,7 +96,7 @@ public class PythonRunnerConfig { public final IProject project; public final IPath[] resource; public final IPath interpreter; - public final IInterpreterInfo interpreterLocation; + public final IInterpreterInfo interpreterInfo; private final String arguments; public final File workingDirectory; public final String pythonpathUsed; @@ -399,8 +400,8 @@ public PythonRunnerConfig(ILaunchConfiguration conf, String mode, String run, acceptTimeout = PydevPrefs.getEclipsePreferences().getInt(PyDevEditorPreferences.CONNECT_TIMEOUT, PyDevEditorPreferences.DEFAULT_CONNECT_TIMEOUT); - interpreterLocation = getInterpreterLocation(conf, pythonNature, this.getRelatedInterpreterManager()); - interpreter = getInterpreter(interpreterLocation, conf, pythonNature); + interpreterInfo = getInterpreterLocation(conf, pythonNature, this.getRelatedInterpreterManager()); + interpreter = getInterpreter(interpreterInfo, conf, pythonNature); //make the environment ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); @@ -418,14 +419,14 @@ public PythonRunnerConfig(ILaunchConfiguration conf, String mode, String run, if (envp == null) { //ok, the user has done nothing to the environment, just get all the default environment which has the pythonpath in it - envp = SimpleRunner.getEnvironment(pythonNature, interpreterLocation, manager); + envp = SimpleRunner.getEnvironment(pythonNature, interpreterInfo, manager); } else { //ok, the user has done something to configure it, so, just add the pythonpath to the //current env (if he still didn't do so) Map envMap = conf.getAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, (Map) null); - String pythonpath = SimpleRunner.makePythonPathEnvString(pythonNature, interpreterLocation, manager); + String pythonpath = SimpleRunner.makePythonPathEnvString(pythonNature, interpreterInfo, manager); updateVar(pythonNature, manager, win32, envMap, "PYTHONPATH", pythonpath); if (isJython()) { //Also update the classpath env variable. @@ -440,7 +441,7 @@ public PythonRunnerConfig(ILaunchConfiguration conf, String mode, String run, } //And we also must get the environment variables specified in the interpreter manager. - envp = interpreterLocation.updateEnv(envp, envMap.keySet()); + envp = interpreterInfo.updateEnv(envp, envMap.keySet()); } boolean hasDjangoNature = project.hasNature(PythonNature.DJANGO_NATURE_ID); @@ -1011,16 +1012,19 @@ private void addDebugArgs(List cmdArgs, String vmType, boolean actualRun } } - /** - * @param cmdArgs - * @throws CoreException - */ private void addVmArgs(List cmdArgs) throws CoreException { - String[] vmArguments = getVMArguments(configuration); - if (vmArguments != null) { + String[] vmArguments = getLaunchVMArguments(configuration); + if (vmArguments != null && vmArguments.length > 0) { for (int i = 0; i < vmArguments.length; i++) { cmdArgs.add(vmArguments[i]); } + } else { + // Use default VM arguments from the interpreter. + String vmArgs = interpreterInfo.getVmArgs(); + if (vmArgs != null && vmArgs.length() > 0) { + String[] parsed = parseVmArguments(vmArgs); + cmdArgs.addAll(Arrays.asList(parsed)); + } } } @@ -1028,16 +1032,20 @@ private void addVmArgs(List cmdArgs) throws CoreException { * @return an array with the vm arguments in the given configuration. * @throws CoreException */ - private String[] getVMArguments(ILaunchConfiguration configuration) throws CoreException { + private String[] getLaunchVMArguments(ILaunchConfiguration configuration) throws CoreException { String args = configuration.getAttribute(Constants.ATTR_VM_ARGUMENTS, (String) null); if (args != null && args.trim().length() > 0) { - String expanded = getStringSubstitution(PythonNature.getPythonNature(project)).performStringSubstitution( - args); - return ProcessUtils.parseArguments(expanded); + return parseVmArguments(args); } return null; } + private String[] parseVmArguments(String args) throws CoreException { + String expanded = getStringSubstitution(PythonNature.getPythonNature(project)).performStringSubstitution( + args); + return ProcessUtils.parseArguments(expanded); + } + /** * @return A command line to be shown to the user. Note that this command line should not actually be used for * an execution (only String[] should be passed to Runtie.exec) diff --git a/plugins/org.python.pydev.shared_core/META-INF/MANIFEST.MF b/plugins/org.python.pydev.shared_core/META-INF/MANIFEST.MF index 48410ceb4a..4f43a8deee 100644 --- a/plugins/org.python.pydev.shared_core/META-INF/MANIFEST.MF +++ b/plugins/org.python.pydev.shared_core/META-INF/MANIFEST.MF @@ -133,6 +133,7 @@ Export-Package: org.apache.lucene, org.python.pydev.shared_core.testutils, org.python.pydev.shared_core.threaded_objects_pool, org.python.pydev.shared_core.utils, + org.python.pydev.shared_core.version, org.python.pydev.shared_core.yaml Bundle-Vendor: Brainwy Software Ltda Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/version/Version.java b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/version/Version.java new file mode 100644 index 0000000000..0551403338 --- /dev/null +++ b/plugins/org.python.pydev.shared_core/src/org/python/pydev/shared_core/version/Version.java @@ -0,0 +1,42 @@ +package org.python.pydev.shared_core.version; + +import java.util.StringTokenizer; + +public class Version implements Comparable { + private int major; + private int minor; + private int patch; + + public Version(String versionString) { + StringTokenizer tokenizer = new StringTokenizer(versionString, "."); + if (tokenizer.hasMoreTokens()) { + major = Integer.parseInt(tokenizer.nextToken()); + } + if (tokenizer.hasMoreTokens()) { + minor = Integer.parseInt(tokenizer.nextToken()); + } + if (tokenizer.hasMoreTokens()) { + patch = Integer.parseInt(tokenizer.nextToken()); + } + } + + @Override + public int compareTo(Version other) { + if (this.major != other.major) { + return Integer.compare(this.major, other.major); + } + if (this.minor != other.minor) { + return Integer.compare(this.minor, other.minor); + } + return Integer.compare(this.patch, other.patch); + } + + public boolean isGreaterThanOrEqualTo(Version other) { + return compareTo(other) >= 0; + } + + @Override + public String toString() { + return major + "." + minor + "." + patch; + } +} \ No newline at end of file diff --git a/plugins/org.python.pydev.shared_core/tests/org/python/pydev/shared_core/version/VersionTest.java b/plugins/org.python.pydev.shared_core/tests/org/python/pydev/shared_core/version/VersionTest.java new file mode 100644 index 0000000000..08ba3ed79c --- /dev/null +++ b/plugins/org.python.pydev.shared_core/tests/org/python/pydev/shared_core/version/VersionTest.java @@ -0,0 +1,29 @@ +package org.python.pydev.shared_core.version; + +import junit.framework.TestCase; + +public class VersionTest extends TestCase { + + public void testVersionComparison() { + Version v1 = new Version("1.2"); + Version v2 = new Version("1.2.1"); + Version v3 = new Version("1.2.2"); + Version v4 = new Version("1.3"); + + Version v5 = new Version("2.0"); + Version v6 = new Version("2.0.0"); + + assertTrue(v1.isGreaterThanOrEqualTo(v1)); + assertTrue(v5.isGreaterThanOrEqualTo(v6)); + assertTrue(v5.isGreaterThanOrEqualTo(v4)); + + assertFalse(v1.isGreaterThanOrEqualTo(v2)); + assertFalse(v2.isGreaterThanOrEqualTo(v3)); + assertFalse(v3.isGreaterThanOrEqualTo(v4)); + assertFalse(v4.isGreaterThanOrEqualTo(v5)); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(VersionTest.class); + } +} \ No newline at end of file diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/AbstractInterpreterEditor.java b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/AbstractInterpreterEditor.java index d2f1473610..38040c4191 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/AbstractInterpreterEditor.java +++ b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/AbstractInterpreterEditor.java @@ -38,6 +38,9 @@ import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; @@ -57,6 +60,7 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; @@ -149,6 +153,10 @@ public Map getNameToInfo() { private final Set exeOrJarOfInterpretersWithPredefinedChanged = new HashSet(); private final Set exeOrJarOfInterpretersWithStringSubstitutionChanged = new HashSet(); + private Label vmArgsLabel; + + private Text vmArgsText; + private void clearInfos() { nameToInfo.clear(); exeOrJarOfInterpretersToRestore.clear(); @@ -283,8 +291,58 @@ public void mouseDoubleClick(MouseEvent e) { renameSelection(); } }); + + if (getShowVMArguments()) { + vmArgsLabel = new Label(parent, SWT.LEFT); + vmArgsLabel.setFont(parent.getFont()); + vmArgsLabel.setText("VM arguments"); + vmArgsLabel.addDisposeListener(event -> vmArgsLabel = null); + + vmArgsText = new Text(parent, SWT.SINGLE | SWT.BORDER); + vmArgsText.setFont(parent.getFont()); + vmArgsText.addDisposeListener(event -> vmArgsText = null); + + vmArgsText.addKeyListener(new KeyAdapter() { + + @Override + public void keyReleased(KeyEvent e) { + InterpreterInfo info = workingCopy.getInfo(); + if (info != null) { + info.setVmArgs(vmArgsText.getText()); + } + } + }); + vmArgsText.addFocusListener(new FocusAdapter() { + // Ensure that the value is checked on focus loss in case we + // missed a keyRelease or user hasn't released key. + // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=214716 + @Override + public void focusLost(FocusEvent e) { + InterpreterInfo info = workingCopy.getInfo(); + if (info != null) { + info.setVmArgs(vmArgsText.getText()); + } + } + }); + + GridData gd = new GridData(); + gd.horizontalAlignment = SWT.FILL; + gd.verticalAlignment = SWT.FILL; + gd.grabExcessVerticalSpace = true; + gd.horizontalSpan = 2; + vmArgsLabel.setLayoutData(gd); + + gd = new GridData(); + gd.horizontalAlignment = SWT.FILL; + gd.grabExcessVerticalSpace = true; + gd.horizontalSpan = 2; + vmArgsText.setLayoutData(gd); + } + } + protected abstract boolean getShowVMArguments(); + private void renameSelection() { int index = getSelectionIndex(); if (index >= 0) { @@ -952,6 +1010,9 @@ private void fillPathItemsFromName(String name) { this.predefinedCompletions.update(info); workingCopy.setInfo(info); } + if (this.getShowVMArguments()) { + this.vmArgsText.setText(info.getVmArgs()); + } if (this.getShowPackageTab()) { packageTab.setInfo(info); } diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/IronpythonInterpreterEditor.java b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/IronpythonInterpreterEditor.java index 0707542a26..9b4a172656 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/IronpythonInterpreterEditor.java +++ b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/IronpythonInterpreterEditor.java @@ -41,4 +41,8 @@ protected boolean getShowPackageTab() { return false; } + @Override + protected boolean getShowVMArguments() { + return false; + } } diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/JythonInterpreterEditor.java b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/JythonInterpreterEditor.java index b121a60f3c..afba61f589 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/JythonInterpreterEditor.java +++ b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/JythonInterpreterEditor.java @@ -42,4 +42,9 @@ protected boolean getShowPackageTab() { return false; } + @Override + protected boolean getShowVMArguments() { + return false; + } + } \ No newline at end of file diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/PythonInterpreterEditor.java b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/PythonInterpreterEditor.java index 1834e39420..2df864f293 100644 --- a/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/PythonInterpreterEditor.java +++ b/plugins/org.python.pydev/src/org/python/pydev/ui/pythonpathconf/PythonInterpreterEditor.java @@ -44,4 +44,9 @@ protected boolean getShowPackageTab() { return true; } + @Override + protected boolean getShowVMArguments() { + return true; + } + } diff --git a/plugins/org.python.pydev/tests/org/python/pydev/ast/interpreter_managers/InterpreterInfoTest.java b/plugins/org.python.pydev/tests/org/python/pydev/ast/interpreter_managers/InterpreterInfoTest.java index 75ea2502f9..d62d5a22b5 100644 --- a/plugins/org.python.pydev/tests/org/python/pydev/ast/interpreter_managers/InterpreterInfoTest.java +++ b/plugins/org.python.pydev/tests/org/python/pydev/ast/interpreter_managers/InterpreterInfoTest.java @@ -314,6 +314,24 @@ public void testInterpreterInfoOutputWithEncoding() throws Exception { assertEquals(i1, i2); } + public void testInterpreterInfoVMArgs() throws Exception { + InterpreterInfo info = new InterpreterInfo("2.4", "C:\\bin\\Python24\\python.exe", new ArrayList<>()); + info.setVmArgs("-Xfoobar"); + + InterpreterInfo info2 = new InterpreterInfo("2.4", "C:\\bin\\Python24\\python.exe", new ArrayList<>()); + + assertNotEquals(info, info2); + info2.setVmArgs("-Xfoobar"); + assertEquals(info, info2); + info2.setVmArgs(""); + assertNotEquals(info, info2); + + String s = info.toString(); + InterpreterInfo withVMArgs = InterpreterInfo.fromString(s, false); + assertEquals("-Xfoobar", withVMArgs.getVmArgs()); + assertEquals(info, withVMArgs); + } + public void testInterpreterInfoOutputWithGarbageBeforeAfterXML() throws Exception { //To generate output: