diff --git a/plugins/org.python.pydev.core/META-INF/MANIFEST.MF b/plugins/org.python.pydev.core/META-INF/MANIFEST.MF
index 2f2baea0c7..6650905252 100644
--- a/plugins/org.python.pydev.core/META-INF/MANIFEST.MF
+++ b/plugins/org.python.pydev.core/META-INF/MANIFEST.MF
@@ -34,6 +34,7 @@ Export-Package: org.python.copiedfromeclipsesrc,
org.python.pydev.core.log,
org.python.pydev.core.logging,
org.python.pydev.core.nature,
+ org.python.pydev.core.package_manager,
org.python.pydev.core.parser,
org.python.pydev.core.partition,
org.python.pydev.core.performanceeval,
diff --git a/plugins/org.python.pydev.core/pysrc/tests_python/my_django_proj_17/.pydevproject b/plugins/org.python.pydev.core/pysrc/tests_python/my_django_proj_17/.pydevproject
index 6e842d0158..594f7b2b11 100644
--- a/plugins/org.python.pydev.core/pysrc/tests_python/my_django_proj_17/.pydevproject
+++ b/plugins/org.python.pydev.core/pysrc/tests_python/my_django_proj_17/.pydevproject
@@ -1,12 +1,22 @@
-
-DJANGO_MANAGE_LOCATION
-manage.py
-
-
-/${PROJECT_DIR_NAME}
-
-python 2.7
-Default
+
+
+
+ DJANGO_MANAGE_LOCATION
+
+ manage.py
+
+
+
+
+
+ /${PROJECT_DIR_NAME}
+
+
+
+ python interpreter
+
+ Default
+
diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/BasePackageManager.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/BasePackageManager.java
new file mode 100644
index 0000000000..88d659db27
--- /dev/null
+++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/BasePackageManager.java
@@ -0,0 +1,161 @@
+/**
+ * Base package manager
+ *
+ * Using either poetry or uv, manage the commands available to apply to a project.
+ *
+ * @author Martin Whitehouse
+ */
+package org.python.pydev.core.package_manager;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.runtime.Platform;
+import org.python.pydev.core.log.Log;
+
+/**
+ * Using ProcessBuilder, runs the commands to achieve e.g. `poetry install
+ * package_name`.
+ */
+abstract public class BasePackageManager {
+ public enum Commands {
+ INSTALL("install"), ENV("env"), LOCK("lock"), SYNC("sync"), UPDATE("update");
+
+ private String command;
+
+ private Commands(String command) {
+ this.command = command;
+ }
+
+ public String command() {
+ return this.command;
+ }
+ }
+
+ /**
+ * Argument to use for changing to the project directory.
+ */
+ private String chDirArg;
+
+ /**
+ * Name of the task
+ */
+ protected String taskName;
+
+ /**
+ * Path to the project directory
+ */
+ private String projectRoot;
+
+ /**
+ * Path to the binary
+ */
+ private String binPath;
+
+ /**
+ * Title to use when binary can't be found.
+ */
+ private String errorTitle;
+ /**
+ * Message to display.
+ */
+ private String errorMsg;
+
+ public BasePackageManager(String projectRoot, String name, String prefsName, String chDirAarg) {
+ this.projectRoot = projectRoot;
+ this.chDirArg = chDirAarg;
+ binPath = Platform.getPreferencesService().getString("org.python.pydev", prefsName, null, null);
+ File f = null;
+ if (binPath != null) {
+ f = new File(binPath);
+ }
+ if (binPath == null | !(f != null && f.exists() && !f.isDirectory())) {
+ errorTitle = name + " not configured";
+ errorMsg = "The path to " + name + "could not be found.\n";
+ errorMsg += "Please configure the path to " + name;
+ }
+ }
+
+ private Boolean checkError() {
+ if (errorMsg != null) {
+ Log.log(errorTitle + "\n" + errorMsg);
+ return false;
+ }
+ return true;
+ }
+
+ protected ArrayList getCommandArgs(String args) {
+ return new ArrayList(Arrays.asList(args));
+ }
+
+ protected ArrayList getCommandArgs(String[] args) {
+ ArrayList parsedArgs = new ArrayList();
+ for (String arg : args) {
+ parsedArgs.add(arg);
+ }
+ return parsedArgs;
+ }
+
+ public String add(String appName) {
+ List args = getCommandArgs("add");
+ String[] apps = appName.split(" ");
+ for (String app : apps) {
+ args.add(app);
+ }
+ return runCommand(args);
+ }
+
+ public String lock() {
+ return runCommand(getCommandArgs("lock"));
+ }
+
+ public String sync() {
+ return runCommand(getCommandArgs("sync"));
+ }
+
+ public String remove(String appName) {
+ List args = getCommandArgs("remove");
+ for (String app : appName.split(" ")) {
+ args.add(app);
+ }
+ return runCommand(args);
+ }
+
+ /**
+ * Returns the output of running `poetry args...`
+ *
+ * @param args List of arguments to pass to binPath e.g. "[poetry] env info"
+ * @return The command output
+ */
+ public String runCommand(List args) {
+ String output = null;
+ if (!checkError()) {
+ return output;
+ }
+ ArrayList commandArgs = new ArrayList();
+
+ commandArgs.add(binPath);
+ commandArgs.add(chDirArg);
+ commandArgs.add(projectRoot);
+
+ for (String arg : args) {
+ commandArgs.add(arg);
+ }
+
+ ProcessBuilder pb = new ProcessBuilder(commandArgs);
+ Process p;
+
+ try {
+ p = pb.start();
+ output = new String(p.getInputStream().readAllBytes()).strip();
+ } catch (IOException e) {
+ Log.log(e);
+ }
+
+ return output;
+ }
+
+}
diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/PoetryPackageManager.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/PoetryPackageManager.java
new file mode 100644
index 0000000000..934388f063
--- /dev/null
+++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/PoetryPackageManager.java
@@ -0,0 +1,42 @@
+package org.python.pydev.core.package_manager;
+
+import java.util.ArrayList;
+
+public class PoetryPackageManager extends BasePackageManager {
+ final public static String PREFS_NAME = "POETRY_BIN";
+ private String pythonPath;
+
+ public PoetryPackageManager(String projectRoot) {
+ super(projectRoot, "poetry", PREFS_NAME, "-C");
+ }
+
+ public String install() {
+ return runCommand(getCommandArgs("install"));
+ }
+
+ @Override
+ public String add(String appName) {
+ ArrayList args = getCommandArgs("add");
+ String[] apps = appName.split(" ");
+ for (String app : apps) {
+ args.add(app);
+ }
+ return runCommand(args);
+ }
+
+ public String update() {
+ return runCommand(getCommandArgs("update"));
+ }
+
+ /**
+ * Returns the path to the python executable of a pyproject virtual environment.
+ *
+ * @return the path to python
+ */
+ public String getPython() {
+ if (pythonPath == null) {
+ pythonPath = runCommand(getCommandArgs(new String[] { "env", "info", "-e" }));
+ }
+ return pythonPath;
+ }
+}
diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/UVPackageManager.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/UVPackageManager.java
new file mode 100644
index 0000000000..bda4c5df99
--- /dev/null
+++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/package_manager/UVPackageManager.java
@@ -0,0 +1,22 @@
+package org.python.pydev.core.package_manager;
+
+public class UVPackageManager extends BasePackageManager {
+ final public static String PREFS_NAME = "UV_BIN";
+ private String pythonPath;
+
+ public UVPackageManager(String projectRoot) {
+ super(projectRoot, "uv", PREFS_NAME, "--directory");
+ }
+
+ /**
+ * Returns the path to the python executable of a pyproject virtual environment.
+ *
+ * @return the path to python
+ */
+ public String getPython() {
+ if (pythonPath == null) {
+ pythonPath = runCommand(getCommandArgs(new String[] { "python", "find" }));
+ }
+ return pythonPath;
+ }
+}
diff --git a/plugins/org.python.pydev.core/tests/org/python/pydev/core/TestDependent.linux.properties b/plugins/org.python.pydev.core/tests/org/python/pydev/core/TestDependent.linux.properties
index b71d632805..c2edc63c73 100644
--- a/plugins/org.python.pydev.core/tests/org/python/pydev/core/TestDependent.linux.properties
+++ b/plugins/org.python.pydev.core/tests/org/python/pydev/core/TestDependent.linux.properties
@@ -41,4 +41,3 @@ JAVA_LOCATION=/usr/bin/java
#/usr/bin/cygpath.exe
#CYGWIN_UNIX_CYGPATH_LOCATION=null
-
diff --git a/plugins/org.python.pydev/icons/PythonPoetry.png b/plugins/org.python.pydev/icons/PythonPoetry.png
new file mode 100644
index 0000000000..c22e6fbbc9
Binary files /dev/null and b/plugins/org.python.pydev/icons/PythonPoetry.png differ
diff --git a/plugins/org.python.pydev/icons/PythonPoetry.svg b/plugins/org.python.pydev/icons/PythonPoetry.svg
new file mode 100644
index 0000000000..2dfd64b6ea
--- /dev/null
+++ b/plugins/org.python.pydev/icons/PythonPoetry.svg
@@ -0,0 +1,42 @@
+
diff --git a/plugins/org.python.pydev/icons/uv.svg b/plugins/org.python.pydev/icons/uv.svg
new file mode 100644
index 0000000000..1701389d67
--- /dev/null
+++ b/plugins/org.python.pydev/icons/uv.svg
@@ -0,0 +1,4 @@
+
diff --git a/plugins/org.python.pydev/plugin.xml b/plugins/org.python.pydev/plugin.xml
index a8542c58db..de4b8e2c3d 100644
--- a/plugins/org.python.pydev/plugin.xml
+++ b/plugins/org.python.pydev/plugin.xml
@@ -121,6 +121,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevPrefsInitializer.java b/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevPrefsInitializer.java
index 6bace53f07..56f9d0d44f 100644
--- a/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevPrefsInitializer.java
+++ b/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevPrefsInitializer.java
@@ -37,7 +37,7 @@ public void initializeDefaultPreferences() {
Preferences node = DefaultScope.INSTANCE.getNode(SharedCorePlugin.DEFAULT_PYDEV_PREFERENCES_SCOPE);
node.putInt(IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_PREFERENCES,
- IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_PROJECT_AS_SRC_FOLDER);
+ IWizardNewProjectNameAndLocationPage.PYDEV_NEW_PROJECT_CREATE_SRC_FOLDER);
//comment blocks
node.put(CommentBlocksPreferences.MULTI_BLOCK_COMMENT_CHAR,
@@ -291,6 +291,8 @@ public void initializeDefaultPreferences() {
//root
node.putBoolean(PydevRootPrefs.CHECK_PREFERRED_PYDEV_SETTINGS,
PydevRootPrefs.DEFAULT_CHECK_PREFERRED_PYDEV_SETTINGS);
+ node.put(PydevRootPrefs.POETRY_BIN, PydevRootPrefs.getDefaultPoetryBinPreference());
+ node.put(PydevRootPrefs.UV_BIN, PydevRootPrefs.getDefaultUVBinPreference());
}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevRootPrefs.java b/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevRootPrefs.java
index b8ace46ad6..39d0ddb28d 100644
--- a/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevRootPrefs.java
+++ b/plugins/org.python.pydev/src/org/python/pydev/plugin/preferences/PydevRootPrefs.java
@@ -8,15 +8,19 @@
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.FileFieldEditor;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.python.pydev.core.package_manager.PoetryPackageManager;
+import org.python.pydev.core.package_manager.UVPackageManager;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.preferences.CheckDefaultPreferencesDialog.CheckInfo;
import org.python.pydev.shared_core.string.StringUtils;
+import org.python.pydev.shared_core.utils.PlatformUtils;
import org.python.pydev.shared_ui.dialogs.DialogHelpers;
import org.python.pydev.shared_ui.field_editors.ButtonFieldEditor;
@@ -24,6 +28,8 @@ public class PydevRootPrefs extends FieldEditorPreferencePage implements IWorkbe
public static final String CHECK_PREFERRED_PYDEV_SETTINGS = "CHECK_PREFERRED_PYDEV_SETTINGS";
public static final boolean DEFAULT_CHECK_PREFERRED_PYDEV_SETTINGS = true;
+ public static final String POETRY_BIN = "POETRY_BIN";
+ public static final String UV_BIN = "UV_BIN";
public PydevRootPrefs() {
setDescription(StringUtils.format("PyDev version: %s",
@@ -63,6 +69,8 @@ public void widgetSelected(SelectionEvent e) {
public void widgetDefaultSelected(SelectionEvent e) {
}
}));
+ addField(new FileFieldEditor(PoetryPackageManager.PREFS_NAME, "Poetry path", p));
+ addField(new FileFieldEditor(UVPackageManager.PREFS_NAME, "UV path", p));
}
public static void setCheckPreferredPydevSettings(boolean b) {
@@ -73,4 +81,36 @@ public static boolean getCheckPreferredPydevSettings() {
return PydevPlugin.getDefault().getPreferenceStore().getBoolean(CHECK_PREFERRED_PYDEV_SETTINGS);
}
+ /**
+ * Gets the default poetry install location.
+ *
+ * @return The path to the poetry bin
+ */
+ public static String getDefaultPoetryBinPreference() {
+ String path;
+ if (PlatformUtils.isWindowsPlatform()) {
+ path = System.getenv("APPDATA") + "\\poetry";
+ } else if (PlatformUtils.isMacOsPlatform()) {
+ path = System.getenv("HOME") + "/Library/Application Support/pypoetry";
+ } else {
+ path = System.getenv("HOME") + "/.local/share/pypoetry/venv/bin/poetry";
+ }
+ return path;
+ }
+
+ /**
+ * Gets the default UV install location.
+ *
+ * @return The path to the uv bin
+ */
+ public static String getDefaultUVBinPreference() {
+ String path;
+ if (PlatformUtils.isWindowsPlatform()) {
+ path = System.getenv("APPDATA") + "\\bin\\uv";
+ } else {
+ path = System.getenv("HOME") + "/.local/bin/uv";
+ }
+ return path;
+ }
+
}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyPoetryAction.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyPoetryAction.java
new file mode 100644
index 0000000000..22fd8168f2
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyPoetryAction.java
@@ -0,0 +1,62 @@
+package org.python.pydev.ui.actions.container;
+
+import java.util.Objects;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.python.pydev.core.log.Log;
+import org.python.pydev.core.package_manager.PoetryPackageManager;
+
+public abstract class PyPoetryAction extends PyContainerAction {
+ protected PoetryPackageManager pm;
+
+ @Override
+ protected boolean confirmRun() {
+ return true;
+ }
+
+ @Override
+ protected void afterRun(int resourcesAffected) {
+ }
+
+ @Override
+ protected boolean needsUIThread() {
+ return true;
+ }
+
+ abstract protected String getTaskName();
+
+ @Override
+ protected int doActionOnContainer(IContainer container, IProgressMonitor monitor) {
+ if (monitor.isCanceled()) {
+ return 0;
+ }
+ monitor.beginTask(getTaskName(), 100);
+ monitor.worked(1);
+ IProject project = container.getProject();
+ pm = new PoetryPackageManager(project.getLocation().toString());
+ String result = runCommand();
+ int affected = 1;
+ if (monitor.isCanceled()) {
+ return 0;
+ }
+ monitor.worked(50);
+ if (!Objects.equals(result, "")) {
+ try {
+ project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ } catch (CoreException e) {
+ Log.log(e);
+ }
+ affected = 0;
+ }
+ monitor.worked(100);
+ monitor.done();
+ return affected;
+ }
+
+ abstract protected String runCommand();
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyUVAction.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyUVAction.java
new file mode 100644
index 0000000000..056a586857
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/container/PyUVAction.java
@@ -0,0 +1,61 @@
+package org.python.pydev.ui.actions.container;
+
+import java.util.Objects;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.python.pydev.core.log.Log;
+import org.python.pydev.core.package_manager.UVPackageManager;
+
+public abstract class PyUVAction extends PyContainerAction {
+ protected UVPackageManager pm;
+
+ @Override
+ protected boolean confirmRun() {
+ return true;
+ }
+
+ @Override
+ protected void afterRun(int resourcesAffected) {
+ }
+
+ @Override
+ protected boolean needsUIThread() {
+ return true;
+ }
+
+ abstract protected String getTaskName();
+
+ @Override
+ protected int doActionOnContainer(IContainer container, IProgressMonitor monitor) {
+ if (monitor.isCanceled()) {
+ return 0;
+ }
+ monitor.beginTask(getTaskName(), 100);
+ monitor.worked(1);
+ IProject project = container.getProject();
+ pm = new UVPackageManager(project.getLocation().toString());
+ String result = runCommand();
+ int affected = 1;
+ if (monitor.isCanceled()) {
+ return 0;
+ }
+ monitor.worked(50);
+ if (!Objects.equals(result, "")) {
+ try {
+ project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+ } catch (CoreException e) {
+ Log.log(e);
+ }
+ affected = 0;
+ }
+ monitor.worked(100);
+ monitor.done();
+ return affected;
+ }
+
+ abstract protected String runCommand();
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryAdd.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryAdd.java
new file mode 100644
index 0000000000..f72860b3cd
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryAdd.java
@@ -0,0 +1,33 @@
+/**
+ *
+ */
+package org.python.pydev.ui.actions.project;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.window.Window;
+import org.python.pydev.shared_ui.EditorUtils;
+import org.python.pydev.ui.actions.container.PyPoetryAction;
+
+/**
+ *
+ */
+public class PoetryAdd extends PyPoetryAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Installing python package...";
+ }
+
+ @Override
+ protected String runCommand() {
+ InputDialog dialog = new InputDialog(EditorUtils.getShell(), "App to install",
+ "Name of the python App to install", null, null);
+ int open = dialog.open();
+ if (open != Window.OK) {
+ return "";
+ }
+ String appName = dialog.getValue();
+ return pm.add(appName);
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryInstall.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryInstall.java
new file mode 100644
index 0000000000..0eee709db2
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryInstall.java
@@ -0,0 +1,156 @@
+package org.python.pydev.ui.actions.project;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectNature;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.python.pydev.ast.interpreter_managers.InterpreterManagersAPI;
+import org.python.pydev.core.IInterpreterInfo;
+import org.python.pydev.core.IInterpreterManager;
+import org.python.pydev.core.MisconfigurationException;
+import org.python.pydev.core.PythonNatureWithoutProjectException;
+import org.python.pydev.core.log.Log;
+import org.python.pydev.core.package_manager.PoetryPackageManager;
+import org.python.pydev.plugin.nature.PythonNature;
+import org.python.pydev.shared_ui.EditorUtils;
+import org.python.pydev.ui.actions.container.PyContainerAction;
+
+public class PoetryInstall extends PyContainerAction {
+ private IPath absPath;
+ private IProject project;
+ private String pythonBin;
+
+ @Override
+ protected boolean confirmRun() {
+ // TODO Figure out why the monitor doesn't progress until a dialog is displayed then use the below.
+ // MessageDialog confirm = new MessageDialog(EditorUtils.getShell(), "Install using poetry", null,
+ // "This will install using...", MessageDialog.QUESTION_WITH_CANCEL, 0, new String[] { "OK", "Cancel" });
+ // confirm.open();
+ // if (confirm.getReturnCode() == 1) {
+ // return false;
+ // }
+ return true;
+ }
+
+ @Override
+ protected void afterRun(int resourcesAffected) {
+ }
+
+ @Override
+ protected boolean needsUIThread() {
+ return true;
+ }
+
+ @Override
+ protected int doActionOnContainer(IContainer container, IProgressMonitor monitor) {
+ monitor.beginTask("Installing project with poetry...", 100);
+ MessageDialog confirm = new MessageDialog(EditorUtils.getShell(), "Install using poetry", null,
+ "This will install using...", MessageDialog.QUESTION_WITH_CANCEL, 0, new String[] { "OK", "Cancel" });
+ confirm.open();
+ if (confirm.getReturnCode() == 1) {
+ return 0;
+ }
+ project = container.getProject();
+ absPath = project.getLocation();
+ PoetryPackageManager pm = new PoetryPackageManager(absPath.toString());
+ String result = pm.install();
+ monitor.worked(50);
+ Log.logInfo("Result: " + result);
+ pythonBin = pm.getPython();
+ Log.logInfo("Python found at: " + pythonBin);
+ PythonNature nature = getNature();
+ if (nature == null) {
+ // This should never happen
+ } else {
+ setInterpreter(nature);
+ }
+ monitor.worked(75);
+ try {
+ project.refreshLocal(1, monitor);
+ } catch (CoreException e) {
+ Log.log(e);
+ }
+ monitor.worked(100);
+ monitor.done();
+ return 0;
+ }
+
+ private PythonNature getNature() {
+ IProjectNature nature;
+ try {
+ nature = project.getNature(PythonNature.PYTHON_NATURE_ID);
+ return (PythonNature) nature;
+ } catch (CoreException e) {
+ return null;
+ }
+ }
+
+ private void setInterpreter(PythonNature nature) {
+ IInterpreterInfo interpreter;
+ try {
+ interpreter = nature.getProjectInterpreter();
+ String name = interpreter.getName();
+ Log.logInfo("Name: " + name);
+ } catch (MisconfigurationException e) {
+ String missingInterpeter = "Interpreter: " + project.getName() + " not found";
+ if (Objects.equals("Python not configured.", e.getMessage())) {
+ Log.logInfo("Misconfiguration error, no interpreters configured.");
+ setDefaultInterpreter(nature);
+ } else if (Objects.equals(missingInterpeter, e.getMessage().toString())) {
+ Log.logInfo("Misconfiguration error, project interpreter not configured.");
+ updateInterpererInfo(nature);
+ } else {
+ Log.logInfo("Unknown error: Message: " + e.getMessage());
+ Log.log(e);
+ }
+ } catch (PythonNatureWithoutProjectException e1) {
+ Log.logInfo("No project nature.");
+ Log.log(e1);
+ }
+ }
+
+ private void setDefaultInterpreter(PythonNature nature) {
+ Log.logInfo("Setting default interpreter.");
+ IInterpreterManager pythonInterpreterManager = InterpreterManagersAPI.getPythonInterpreterManager();
+ IInterpreterInfo projectInterpreter = pythonInterpreterManager.createInterpreterInfo(pythonBin,
+ new NullProgressMonitor(), false);
+ projectInterpreter.setName(project.getName());
+ pythonInterpreterManager.setInfos(new IInterpreterInfo[] { projectInterpreter }, null,
+ new NullProgressMonitor());
+ Log.logInfo("Configured!");
+ try {
+ nature.setVersion(PythonNature.PYTHON_VERSION_INTERPRETER, project.getName());
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void updateInterpererInfo(PythonNature nature) {
+ IInterpreterManager pythonInterpreterManager = InterpreterManagersAPI.getPythonInterpreterManager();
+ IInterpreterInfo[] infos = pythonInterpreterManager.getInterpreterInfos();
+ IInterpreterInfo projectInterpreter = pythonInterpreterManager.createInterpreterInfo(pythonBin,
+ new NullProgressMonitor(), false);
+ projectInterpreter.setName(project.getName());
+ Set existing = new HashSet();
+ ArrayList newInfos = new ArrayList();
+ for (IInterpreterInfo info : infos) {
+ existing.add(info.getName());
+ newInfos.add(info);
+ }
+ newInfos.add(projectInterpreter);
+ pythonInterpreterManager.setInfos(newInfos.toArray(new IInterpreterInfo[newInfos.size()]), existing,
+ new NullProgressMonitor());
+
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryLock.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryLock.java
new file mode 100644
index 0000000000..1c9bd6ee82
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryLock.java
@@ -0,0 +1,17 @@
+package org.python.pydev.ui.actions.project;
+
+import org.python.pydev.ui.actions.container.PyPoetryAction;
+
+public class PoetryLock extends PyPoetryAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Locking project dependencies...";
+ }
+
+ @Override
+ protected String runCommand() {
+ return pm.lock();
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryRemove.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryRemove.java
new file mode 100644
index 0000000000..bb476eef90
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryRemove.java
@@ -0,0 +1,32 @@
+/**
+ *
+ */
+package org.python.pydev.ui.actions.project;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.window.Window;
+import org.python.pydev.shared_ui.EditorUtils;
+import org.python.pydev.ui.actions.container.PyPoetryAction;
+
+/**
+ *
+ */
+public class PoetryRemove extends PyPoetryAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Removing python package...";
+ }
+
+ @Override
+ protected String runCommand() {
+ InputDialog dialog = new InputDialog(EditorUtils.getShell(), "App to remove",
+ "Name of the python App to remove", null, null);
+ int open = dialog.open();
+ if (open != Window.OK) {
+ return "";
+ }
+ String appName = dialog.getValue();
+ return pm.remove(appName);
+ }
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetrySync.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetrySync.java
new file mode 100644
index 0000000000..91a1cc9043
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetrySync.java
@@ -0,0 +1,17 @@
+package org.python.pydev.ui.actions.project;
+
+import org.python.pydev.ui.actions.container.PyPoetryAction;
+
+public class PoetrySync extends PyPoetryAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Syncronising project dependencies...";
+ }
+
+ @Override
+ protected String runCommand() {
+ return pm.sync();
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryUpdate.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryUpdate.java
new file mode 100644
index 0000000000..fc85d63dac
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/PoetryUpdate.java
@@ -0,0 +1,22 @@
+/**
+ *
+ */
+package org.python.pydev.ui.actions.project;
+
+import org.python.pydev.ui.actions.container.PyPoetryAction;
+
+/**
+ *
+ */
+public class PoetryUpdate extends PyPoetryAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Updating python packages...";
+ }
+
+ @Override
+ protected String runCommand() {
+ return pm.update();
+ }
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVAdd.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVAdd.java
new file mode 100644
index 0000000000..d6eeef29c1
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVAdd.java
@@ -0,0 +1,33 @@
+/**
+ *
+ */
+package org.python.pydev.ui.actions.project;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.window.Window;
+import org.python.pydev.shared_ui.EditorUtils;
+import org.python.pydev.ui.actions.container.PyUVAction;
+
+/**
+ *
+ */
+public class UVAdd extends PyUVAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Installing python package...";
+ }
+
+ @Override
+ protected String runCommand() {
+ InputDialog dialog = new InputDialog(EditorUtils.getShell(), "App to install",
+ "Name of the python App to install", null, null);
+ int open = dialog.open();
+ if (open != Window.OK) {
+ return "";
+ }
+ String appName = dialog.getValue();
+ return pm.add(appName);
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVLock.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVLock.java
new file mode 100644
index 0000000000..902ca6ee8b
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVLock.java
@@ -0,0 +1,17 @@
+package org.python.pydev.ui.actions.project;
+
+import org.python.pydev.ui.actions.container.PyUVAction;
+
+public class UVLock extends PyUVAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Locking project dependencies...";
+ }
+
+ @Override
+ protected String runCommand() {
+ return pm.lock();
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVRemove.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVRemove.java
new file mode 100644
index 0000000000..ea4d184704
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVRemove.java
@@ -0,0 +1,33 @@
+/**
+ *
+ */
+package org.python.pydev.ui.actions.project;
+
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.window.Window;
+import org.python.pydev.shared_ui.EditorUtils;
+import org.python.pydev.ui.actions.container.PyUVAction;
+
+/**
+ *
+ */
+public class UVRemove extends PyUVAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Installing python package...";
+ }
+
+ @Override
+ protected String runCommand() {
+ InputDialog dialog = new InputDialog(EditorUtils.getShell(), "App to remove",
+ "Name of the python App to remove", null, null);
+ int open = dialog.open();
+ if (open != Window.OK) {
+ return "";
+ }
+ String appName = dialog.getValue();
+ return pm.remove(appName);
+ }
+
+}
diff --git a/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVSync.java b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVSync.java
new file mode 100644
index 0000000000..4a8311d5ab
--- /dev/null
+++ b/plugins/org.python.pydev/src/org/python/pydev/ui/actions/project/UVSync.java
@@ -0,0 +1,17 @@
+package org.python.pydev.ui.actions.project;
+
+import org.python.pydev.ui.actions.container.PyUVAction;
+
+public class UVSync extends PyUVAction {
+
+ @Override
+ protected String getTaskName() {
+ return "Syncronising project dependencies...";
+ }
+
+ @Override
+ protected String runCommand() {
+ return pm.sync();
+ }
+
+}