Thu, 27 Jun 2024 17:50:51 +0200
Added functionality to reinstall single or all pipx managed packages.
--- a/PipxInterface/Pipx.py Thu Jun 27 16:20:56 2024 +0200 +++ b/PipxInterface/Pipx.py Thu Jun 27 17:50:51 2024 +0200 @@ -206,7 +206,7 @@ systemSitePackages=False ): """ - Public method + Public method to install a list of packages with the given options. @param packages list of packages to install @type list of str @@ -229,8 +229,8 @@ args += ["--index-url", indexUrl] if interpreterVersion: args += ["--python", interpreterVersion] - if fetchMissingInterpreter: - args.append("--fetch-missing-python") + if fetchMissingInterpreter: + args.append("--fetch-missing-python") if forceVenvModification: args.append("--force") if systemSitePackages: @@ -250,7 +250,8 @@ systemSitePackages=False ): """ - Public method + Public method to install all packages define by a given spec metadata file + with given options. @param specFile path of the spec metadata file @type str @@ -273,8 +274,8 @@ args += ["--index-url", indexUrl] if interpreterVersion: args += ["--python", interpreterVersion] - if fetchMissingInterpreter: - args.append("--fetch-missing-python") + if fetchMissingInterpreter: + args.append("--fetch-missing-python") if forceVenvModification: args.append("--force") if systemSitePackages: @@ -306,6 +307,68 @@ else: return False, output + def reinstallPackage( + self, + package, + interpreterVersion="", + fetchMissingInterpreter=False, + ): + """ + Public method to reinstall the given package with given options + + @param package name of the package to reinstall + @type str + @param interpreterVersion version of the Python interpreter (defaults to "") + @type str (optional) + @param fetchMissingInterpreter flag indicating to fetch a standalone Python + build from GitHub if the specified Python version is not found locally + on the system (defaults to False) + @type bool (optional) + """ + args = ["reinstall"] + if interpreterVersion: + args += ["--python", interpreterVersion] + if fetchMissingInterpreter: + args.append("--fetch-missing-python") + args.append(package) + dia = PipxExecDialog(self.tr("Re-Install Package")) + res = dia.startProcess(self.__getPipxExecutable(), args) + if res: + dia.exec() + + def reinstallAllPackages( + self, + interpreterVersion="", + fetchMissingInterpreter=False, + skipPackages=None, + ): + """ + Public method to reinstall all packages with given options + + @param package name of the package to reinstall + @type str + @param interpreterVersion version of the Python interpreter (defaults to "") + @type str (optional) + @param fetchMissingInterpreter flag indicating to fetch a standalone Python + build from GitHub if the specified Python version is not found locally + on the system (defaults to False) + @type bool (optional) + @param skipPackages list of packages to be skipped by the 'reinstall-all' + command (defaults to None) + @type list of str (optional) + """ + args = ["reinstall-all"] + if interpreterVersion: + args += ["--python", interpreterVersion] + if fetchMissingInterpreter: + args.append("--fetch-missing-python") + if skipPackages: + args += ["--skip"] + skipPackages + dia = PipxExecDialog(self.tr("Re-Install All Packages")) + res = dia.startProcess(self.__getPipxExecutable(), args) + if res: + dia.exec() + def uninstallPackage(self, package): """ Public method to uninstall the given package.
--- a/PipxInterface/PipxPackagesInputDialog.py Thu Jun 27 16:20:56 2024 +0200 +++ b/PipxInterface/PipxPackagesInputDialog.py Thu Jun 27 17:50:51 2024 +0200 @@ -60,10 +60,8 @@ indicating to give access to the system site-packages directory. @rtype tuple of (list of str, str, bool, bool, bool) """ - packages = [p.strip() for p in self.packagesEdit.text().split()] - return ( - packages, + [p.strip() for p in self.packagesEdit.text().split()], self.interpreterVersionEdit.text().strip(), self.fetchMissingCheckBox.isChecked(), self.forceCheckBox.isChecked(),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PipxInterface/PipxReinstallDialog.py Thu Jun 27 17:50:51 2024 +0200 @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2024 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to enter the desired Python interpreter version. +""" + +from PyQt6.QtWidgets import QDialog + +from .Ui_PipxReinstallDialog import Ui_PipxReinstallDialog + + +class PipxReinstallDialog(QDialog, Ui_PipxReinstallDialog): + """ + Class implementing a dialog to enter the desired Python interpreter version. + """ + + def __init__(self, reinstallAll=False, parent=None): + """ + Constructor + + @param reinstallAll flag indicating to get the parameters for a 'reinstall-all' + action + @type bool + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + self.setupUi(self) + + self.skipGroupBox.setVisible(reinstallAll) + + self.fetchMissingCheckBox.setChecked(True) + + msh = self.minimumSizeHint() + self.resize(max(self.width(), msh.width()), msh.height()) + + def getData(self): + """ + Public method to get the entered data. + + @return tuple containing the desired Python version, a flag indicating + to fetch a standalone Python build from GitHub if the specified Python + version is not found locally on the system and a list of packages to skip + in case of a 'reinstall-all' + @rtype tuple of (str, bool, list of str) + """ + return ( + self.interpreterVersionEdit.text(), + self.fetchMissingCheckBox.isChecked(), + [p.strip() for p in self.packagesEdit.text().split()], + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PipxInterface/PipxReinstallDialog.ui Thu Jun 27 17:50:51 2024 +0200 @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PipxReinstallDialog</class> + <widget class="QDialog" name="PipxReinstallDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>268</height> + </rect> + </property> + <property name="windowTitle"> + <string>Package Re-Installation</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="skipGroupBox"> + <property name="title"> + <string>Skip Packages</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Package Specifications (separated by whitespace):</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="packagesEdit"/> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="interpreterGroupBox"> + <property name="title"> + <string>Standalone Python Interpreter</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string><b>Note:</b> Leave this entry empty to use the default Python interpreter.</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Version:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="interpreterVersionEdit"> + <property name="toolTip"> + <string>Enter the version number of the Python interpreter to be used.</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QCheckBox" name="fetchMissingCheckBox"> + <property name="toolTip"> + <string>Select to fetch a standalone Python build from GitHub if the specified Python version is not found locally on the system.</string> + </property> + <property name="text"> + <string>Fetch missing Python interpreter</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>packagesEdit</tabstop> + <tabstop>interpreterVersionEdit</tabstop> + <tabstop>fetchMissingCheckBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>PipxReinstallDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PipxReinstallDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
--- a/PipxInterface/PipxWidget.py Thu Jun 27 16:20:56 2024 +0200 +++ b/PipxInterface/PipxWidget.py Thu Jun 27 17:50:51 2024 +0200 @@ -107,7 +107,7 @@ ) self.__installSubmenu.addSeparator() self.__reinstallPackagesAct = self.__installSubmenu.addAction( - self.tr("Re-Install Selected Packages"), self.__reinstallPackages + self.tr("Re-Install Selected Package"), self.__reinstallPackage ) self.__reinstallAllPackagesAct = self.__installSubmenu.addAction( self.tr("Re-Install All Packages"), self.__reinstallAllPackages @@ -261,20 +261,53 @@ ) @pyqtSlot() - def __reinstallPackages(self): + def __reinstallPackage(self): + """ + Private slot to force a re-installation of the selected package. """ - Private slot to force a re-installation of the selected packages. - """ - # TODO: not implemented yet - pass + from .PipxReinstallDialog import PipxReinstallDialog + + package = self.__selectedPackages()[0] + yes = EricMessageBox.yesNo( + self, + self.tr("Re-Install Package"), + self.tr( + "<p>Shall the package <b>{0}</b> really be reinstalled?</p>" + ).format(package), + ) + if yes: + dlg = PipxReinstallDialog(reinstallAll=False, parent=self) + if dlg.exec() == QDialog.DialogCode.Accepted: + pyVersion, fetchMissing, _ = dlg.getData() + self.__pipx.reinstallPackage( + package, + interpreterVersion=pyVersion, + fetchMissingInterpreter=fetchMissing, + ) + self.on_refreshButton_clicked() @pyqtSlot() def __reinstallAllPackages(self): """ Private slot to force a re-installation of all packages. """ - # TODO: not implemented yet - pass + from .PipxReinstallDialog import PipxReinstallDialog + + yes = EricMessageBox.yesNo( + self, + self.tr("Re-Install All Packages"), + self.tr("""Do you really want to reinstall all packages?"""), + ) + if yes: + dlg = PipxReinstallDialog(reinstallAll=True, parent=self) + if dlg.exec() == QDialog.DialogCode.Accepted: + pyVersion, fetchMissing, skipList = dlg.getData() + self.__pipx.reinstallAllPackages( + interpreterVersion=pyVersion, + fetchMissingInterpreter=fetchMissing, + skipPackages=skipList, + ) + self.on_refreshButton_clicked() @pyqtSlot() def __upgradePackage(self):
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PipxInterface/Ui_PipxReinstallDialog.py Thu Jun 27 17:50:51 2024 +0200 @@ -0,0 +1,70 @@ +# Form implementation generated from reading ui file 'PipxInterface/PipxReinstallDialog.ui' +# +# Created by: PyQt6 UI code generator 6.7.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_PipxReinstallDialog(object): + def setupUi(self, PipxReinstallDialog): + PipxReinstallDialog.setObjectName("PipxReinstallDialog") + PipxReinstallDialog.resize(600, 268) + PipxReinstallDialog.setSizeGripEnabled(True) + self.verticalLayout_2 = QtWidgets.QVBoxLayout(PipxReinstallDialog) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.skipGroupBox = QtWidgets.QGroupBox(parent=PipxReinstallDialog) + self.skipGroupBox.setObjectName("skipGroupBox") + self.verticalLayout = QtWidgets.QVBoxLayout(self.skipGroupBox) + self.verticalLayout.setObjectName("verticalLayout") + self.label_3 = QtWidgets.QLabel(parent=self.skipGroupBox) + self.label_3.setObjectName("label_3") + self.verticalLayout.addWidget(self.label_3) + self.packagesEdit = QtWidgets.QLineEdit(parent=self.skipGroupBox) + self.packagesEdit.setObjectName("packagesEdit") + self.verticalLayout.addWidget(self.packagesEdit) + self.verticalLayout_2.addWidget(self.skipGroupBox) + self.interpreterGroupBox = QtWidgets.QGroupBox(parent=PipxReinstallDialog) + self.interpreterGroupBox.setObjectName("interpreterGroupBox") + self.gridLayout = QtWidgets.QGridLayout(self.interpreterGroupBox) + self.gridLayout.setObjectName("gridLayout") + self.label_2 = QtWidgets.QLabel(parent=self.interpreterGroupBox) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 0, 0, 1, 2) + self.label_4 = QtWidgets.QLabel(parent=self.interpreterGroupBox) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 1, 0, 1, 1) + self.interpreterVersionEdit = QtWidgets.QLineEdit(parent=self.interpreterGroupBox) + self.interpreterVersionEdit.setObjectName("interpreterVersionEdit") + self.gridLayout.addWidget(self.interpreterVersionEdit, 1, 1, 1, 1) + self.fetchMissingCheckBox = QtWidgets.QCheckBox(parent=self.interpreterGroupBox) + self.fetchMissingCheckBox.setObjectName("fetchMissingCheckBox") + self.gridLayout.addWidget(self.fetchMissingCheckBox, 2, 0, 1, 2) + self.verticalLayout_2.addWidget(self.interpreterGroupBox) + self.buttonBox = QtWidgets.QDialogButtonBox(parent=PipxReinstallDialog) + self.buttonBox.setOrientation(QtCore.Qt.Orientation.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout_2.addWidget(self.buttonBox) + + self.retranslateUi(PipxReinstallDialog) + self.buttonBox.accepted.connect(PipxReinstallDialog.accept) # type: ignore + self.buttonBox.rejected.connect(PipxReinstallDialog.reject) # type: ignore + QtCore.QMetaObject.connectSlotsByName(PipxReinstallDialog) + PipxReinstallDialog.setTabOrder(self.packagesEdit, self.interpreterVersionEdit) + PipxReinstallDialog.setTabOrder(self.interpreterVersionEdit, self.fetchMissingCheckBox) + + def retranslateUi(self, PipxReinstallDialog): + _translate = QtCore.QCoreApplication.translate + PipxReinstallDialog.setWindowTitle(_translate("PipxReinstallDialog", "Package Re-Installation")) + self.skipGroupBox.setTitle(_translate("PipxReinstallDialog", "Skip Packages")) + self.label_3.setText(_translate("PipxReinstallDialog", "Package Specifications (separated by whitespace):")) + self.interpreterGroupBox.setTitle(_translate("PipxReinstallDialog", "Standalone Python Interpreter")) + self.label_2.setText(_translate("PipxReinstallDialog", "<b>Note:</b> Leave this entry empty to use the default Python interpreter.")) + self.label_4.setText(_translate("PipxReinstallDialog", "Version:")) + self.interpreterVersionEdit.setToolTip(_translate("PipxReinstallDialog", "Enter the version number of the Python interpreter to be used.")) + self.fetchMissingCheckBox.setToolTip(_translate("PipxReinstallDialog", "Select to fetch a standalone Python build from GitHub if the specified Python version is not found locally on the system.")) + self.fetchMissingCheckBox.setText(_translate("PipxReinstallDialog", "Fetch missing Python interpreter"))
--- a/PluginPipxInterface.epj Thu Jun 27 16:20:56 2024 +0200 +++ b/PluginPipxInterface.epj Thu Jun 27 17:50:51 2024 +0200 @@ -42,6 +42,7 @@ "PipxInterface/PipxAppStartDialog.ui", "PipxInterface/PipxExecDialog.ui", "PipxInterface/PipxPackagesInputDialog.ui", + "PipxInterface/PipxReinstallDialog.ui", "PipxInterface/PipxSpecInputDialog.ui", "PipxInterface/PipxWidget.ui" ], @@ -130,11 +131,13 @@ "PipxInterface/PipxAppStartDialog.py", "PipxInterface/PipxExecDialog.py", "PipxInterface/PipxPackagesInputDialog.py", + "PipxInterface/PipxReinstallDialog.py", "PipxInterface/PipxSpecInputDialog.py", "PipxInterface/PipxWidget.py", "PipxInterface/Ui_PipxAppStartDialog.py", "PipxInterface/Ui_PipxExecDialog.py", "PipxInterface/Ui_PipxPackagesInputDialog.py", + "PipxInterface/Ui_PipxReinstallDialog.py", "PipxInterface/Ui_PipxSpecInputDialog.py", "PipxInterface/Ui_PipxWidget.py", "PipxInterface/__init__.py",