Plugins/UiExtensionPlugins/PipInterface/Pip.py

changeset 6011
e6af0dcfbb35
child 6026
4773c9469880
equal deleted inserted replaced
6010:7ef7d47a0ad5 6011:e6af0dcfbb35
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2015 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Package implementing the pip GUI logic.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode # __IGNORE_EXCEPTION__
13 except NameError:
14 pass
15
16 import os
17 import re
18 import sys
19
20 from PyQt5.QtCore import pyqtSlot, QObject, QProcess, QDir
21 from PyQt5.QtWidgets import QMenu, QInputDialog, QDialog
22
23 from E5Gui import E5MessageBox, E5FileDialog
24 from E5Gui.E5Action import E5Action
25 from E5Gui.E5Application import e5App
26
27 from .PipDialog import PipDialog
28
29 import Preferences
30 import Globals
31
32
33 class Pip(QObject):
34 """
35 Class implementing the pip GUI logic.
36 """
37 def __init__(self, plugin, parent=None):
38 """
39 Constructor
40
41 @param plugin reference to the plugin object
42 @param parent parent (QObject)
43 """
44 super(Pip, self).__init__(parent)
45
46 self.__plugin = plugin
47 self.__ui = parent
48
49 self.__menus = {} # dictionary with references to menus
50
51 self.__plugin.currentPipChanged.connect(self.__handleTearOffMenu)
52
53 def initActions(self):
54 """
55 Public method to define the Django actions.
56 """
57 self.actions = []
58
59 self.selectExecutableAct = E5Action(
60 self.tr('pip Executable'),
61 self.tr('pip &Executable'),
62 0, 0,
63 self, 'pip_select_executable')
64 self.selectExecutableAct.setStatusTip(self.tr(
65 'Selects the pip executable to be used'))
66 self.selectExecutableAct.setWhatsThis(self.tr(
67 """<b>pip Executable</b>"""
68 """<p>This selects the pip executable to be used. Multiple"""
69 """ executables can be pre-configured via the configuration"""
70 """ dialog.</p>"""
71 ))
72 self.selectExecutableAct.triggered.connect(self.__selectPipExecutable)
73 self.actions.append(self.selectExecutableAct)
74
75 ##############################################
76 ## Actions for listing packages
77 ##############################################
78
79 self.listPackagesAct = E5Action(
80 self.tr('List Installed Packages'),
81 self.tr('&List Installed Packages...'),
82 0, 0,
83 self, 'pip_list_packages')
84 self.listPackagesAct.setStatusTip(self.tr(
85 'List all installed packages with versions'))
86 self.listPackagesAct.setWhatsThis(self.tr(
87 """<b>List Installed Packages</b>"""
88 """<p>This lists all the installed packages together"""
89 """ with their versions.</p>"""
90 ))
91 self.listPackagesAct.triggered.connect(self.__listPackages)
92 self.actions.append(self.listPackagesAct)
93
94 self.listUptodatePackagesAct = E5Action(
95 self.tr('List Up-to-date Packages'),
96 self.tr('List Up-to-&date Packages...'),
97 0, 0,
98 self, 'pip_list_uptodate_packages')
99 self.listUptodatePackagesAct.setStatusTip(self.tr(
100 'List all installed, up-to-date packages with versions'))
101 self.listUptodatePackagesAct.setWhatsThis(self.tr(
102 """<b>List Up-to-date Packages</b>"""
103 """<p>This lists all the installed, up-to-date packages together"""
104 """ with their versions.</p>"""
105 ))
106 self.listUptodatePackagesAct.triggered.connect(
107 self.__listUptodatePackages)
108 self.actions.append(self.listUptodatePackagesAct)
109
110 self.listOutdatedPackagesAct = E5Action(
111 self.tr('List Outdated Packages'),
112 self.tr('List &Outdated Packages...'),
113 0, 0,
114 self, 'pip_list_outdated_packages')
115 self.listOutdatedPackagesAct.setStatusTip(self.tr(
116 'List all installed, outdated packages with versions'))
117 self.listOutdatedPackagesAct.setWhatsThis(self.tr(
118 """<b>List Up-to-date Packages</b>"""
119 """<p>This lists all the installed, outdated packages together"""
120 """ with their current and latest versions.</p>"""
121 ))
122 self.listOutdatedPackagesAct.triggered.connect(
123 self.__listOutdatedPackages)
124 self.actions.append(self.listOutdatedPackagesAct)
125
126 ##############################################
127 ## Actions for installing packages
128 ##############################################
129
130 self.installPackagesAct = E5Action(
131 self.tr('Install Packages'),
132 self.tr('&Install Packages'),
133 0, 0,
134 self, 'pip_install_packages')
135 self.installPackagesAct.setStatusTip(self.tr(
136 'Install packages according to user input'))
137 self.installPackagesAct.setWhatsThis(self.tr(
138 """<b>Install Packages</b>"""
139 """<p>This installs packages according to user input.</p>"""
140 ))
141 self.installPackagesAct.triggered.connect(self.__installPackages)
142 self.actions.append(self.installPackagesAct)
143
144 self.installRequirementsAct = E5Action(
145 self.tr('Install Requirements'),
146 self.tr('Install Requirements'),
147 0, 0,
148 self, 'pip_install_requirements')
149 self.installRequirementsAct.setStatusTip(self.tr(
150 'Install packages according to a requirements file'))
151 self.installRequirementsAct.setWhatsThis(self.tr(
152 """<b>Install Requirements</b>"""
153 """<p>This installs packages according to a requirements"""
154 """ file.</p>"""
155 ))
156 self.installRequirementsAct.triggered.connect(
157 self.__installRequirements)
158 self.actions.append(self.installRequirementsAct)
159
160 self.installPipAct = E5Action(
161 self.tr('Install Pip'),
162 self.tr('Install Pip'),
163 0, 0,
164 self, 'pip_install_pip')
165 self.installPipAct.setStatusTip(self.tr(
166 'Install the pip package itself'))
167 self.installPipAct.setWhatsThis(self.tr(
168 """<b>Install Pip</b>"""
169 """<p>This install the pip package itself.</p>"""
170 ))
171 self.installPipAct.triggered.connect(self.__installPip)
172 self.actions.append(self.installPipAct)
173
174 self.repairPipAct = E5Action(
175 self.tr('Repair Pip'),
176 self.tr('Repair Pip'),
177 0, 0,
178 self, 'pip_repair_pip')
179 self.repairPipAct.setStatusTip(self.tr(
180 'Repair the pip package'))
181 self.repairPipAct.setWhatsThis(self.tr(
182 """<b>Repair Pip</b>"""
183 """<p>This repairs the pip package by re-installing it.</p>"""
184 ))
185 self.repairPipAct.triggered.connect(self.__repairPip)
186 self.actions.append(self.repairPipAct)
187
188 self.upgradePipAct = E5Action(
189 self.tr('Upgrade Pip'),
190 self.tr('Upgrade &Pip'),
191 0, 0,
192 self, 'pip_upgrade_pip')
193 self.upgradePipAct.setStatusTip(self.tr(
194 'Upgrade the pip package itself'))
195 self.upgradePipAct.setWhatsThis(self.tr(
196 """<b>Upgrade Pip</b>"""
197 """<p>This upgrades the pip package itself.</p>"""
198 ))
199 self.upgradePipAct.triggered.connect(self.upgradePip)
200 self.actions.append(self.upgradePipAct)
201
202 self.upgradePackagesAct = E5Action(
203 self.tr('Upgrade Packages'),
204 self.tr('&Upgrade Packages'),
205 0, 0,
206 self, 'pip_upgrade_packages')
207 self.upgradePackagesAct.setStatusTip(self.tr(
208 'Upgrade packages according to user input'))
209 self.upgradePackagesAct.setWhatsThis(self.tr(
210 """<b>Upgrade Packages</b>"""
211 """<p>This upgrades packages according to user input.</p>"""
212 ))
213 self.upgradePackagesAct.triggered.connect(self.__upgradePackages)
214 self.actions.append(self.upgradePackagesAct)
215
216 ##############################################
217 ## Actions for uninstalling packages
218 ##############################################
219
220 self.uninstallPackagesAct = E5Action(
221 self.tr('Uninstall Packages'),
222 self.tr('Uninstall Packages'),
223 0, 0,
224 self, 'pip_uninstall_packages')
225 self.uninstallPackagesAct.setStatusTip(self.tr(
226 'Uninstall packages according to user input'))
227 self.uninstallPackagesAct.setWhatsThis(self.tr(
228 """<b>Uninstall Packages</b>"""
229 """<p>This uninstalls packages according to user input.</p>"""
230 ))
231 self.uninstallPackagesAct.triggered.connect(self.__uninstallPackages)
232 self.actions.append(self.uninstallPackagesAct)
233
234 self.uninstallRequirementsAct = E5Action(
235 self.tr('Uninstall Requirements'),
236 self.tr('Uninstall Requirements'),
237 0, 0,
238 self, 'pip_uninstall_requirements')
239 self.uninstallRequirementsAct.setStatusTip(self.tr(
240 'Uninstall packages according to a requirements file'))
241 self.uninstallRequirementsAct.setWhatsThis(self.tr(
242 """<b>Uninstall Requirements</b>"""
243 """<p>This uninstalls packages according to a requirements"""
244 """ file.</p>"""
245 ))
246 self.uninstallRequirementsAct.triggered.connect(
247 self.__uninstallRequirements)
248 self.actions.append(self.uninstallRequirementsAct)
249
250 ##############################################
251 ## Actions for generating requirements files
252 ##############################################
253
254 self.generateRequirementsAct = E5Action(
255 self.tr('Generate Requirements'),
256 self.tr('&Generate Requirements...'),
257 0, 0,
258 self, 'pip_generate_requirements')
259 self.generateRequirementsAct.setStatusTip(self.tr(
260 'Generate the contents of a requirements file'))
261 self.generateRequirementsAct.setWhatsThis(self.tr(
262 """<b>Generate Requirements</b>"""
263 """<p>This generates the contents of a requirements file.</p>"""
264 ))
265 self.generateRequirementsAct.triggered.connect(
266 self.__generateRequirements)
267 self.actions.append(self.generateRequirementsAct)
268
269 ##############################################
270 ## Actions for generating requirements files
271 ##############################################
272
273 self.searchPyPIAct = E5Action(
274 self.tr('Search PyPI'),
275 self.tr('&Search PyPI...'),
276 0, 0,
277 self, 'pip_search_pypi')
278 self.searchPyPIAct.setStatusTip(self.tr(
279 'Open a dialog to search the Python Package Index'))
280 self.searchPyPIAct.setWhatsThis(self.tr(
281 """<b>Search PyPI</b>"""
282 """<p>This opens a dialog to search the Python Package"""
283 """ Index.</p>"""
284 ))
285 self.searchPyPIAct.triggered.connect(self.__searchPyPI)
286 self.actions.append(self.searchPyPIAct)
287
288 ##############################################
289 ## Actions for editing configuration files
290 ##############################################
291
292 self.editUserConfigAct = E5Action(
293 self.tr('Edit User Configuration'),
294 self.tr('Edit User Configuration...'),
295 0, 0,
296 self, 'pip_edit_user_config')
297 self.editUserConfigAct.setStatusTip(self.tr(
298 'Open the per user configuration file in an editor'))
299 self.editUserConfigAct.setWhatsThis(self.tr(
300 """<b>Edit User Configuration</b>"""
301 """<p>This opens the per user configuration file in an editor."""
302 """</p>"""
303 ))
304 self.editUserConfigAct.triggered.connect(self.__editUserConfiguration)
305 self.actions.append(self.editUserConfigAct)
306
307 self.editVirtualenvConfigAct = E5Action(
308 self.tr('Edit Current Virtualenv Configuration'),
309 self.tr('Edit Current Virtualenv Configuration...'),
310 0, 0,
311 self, 'pip_edit_virtualenv_config')
312 self.editVirtualenvConfigAct.setStatusTip(self.tr(
313 'Open the current virtualenv configuration file in an editor'))
314 self.editVirtualenvConfigAct.setWhatsThis(self.tr(
315 """<b>Edit Current Virtualenv Configuration</b>"""
316 """<p>This opens the current virtualenv configuration file in"""
317 """ an editor. </p>"""
318 ))
319 self.editVirtualenvConfigAct.triggered.connect(
320 self.__editVirtualenvConfiguration)
321 self.actions.append(self.editVirtualenvConfigAct)
322
323 self.pipConfigAct = E5Action(
324 self.tr('Configure'),
325 self.tr('Configure...'),
326 0, 0, self, 'pip_configure')
327 self.pipConfigAct.setStatusTip(self.tr(
328 'Show the configuration dialog with the Python Package Management'
329 ' page selected'
330 ))
331 self.pipConfigAct.setWhatsThis(self.tr(
332 """<b>Configure</b>"""
333 """<p>Show the configuration dialog with the Python Package"""
334 """ Management page selected.</p>"""
335 ))
336 self.pipConfigAct.triggered.connect(self.__pipConfigure)
337 self.actions.append(self.pipConfigAct)
338
339 def initMenu(self):
340 """
341 Public slot to initialize the Django menu.
342
343 @return the menu generated (QMenu)
344 """
345 self.__menus = {} # clear menus references
346
347 menu = QMenu(self.tr('P&ython Package Management'), self.__ui)
348 menu.setTearOffEnabled(True)
349
350 menu.addAction(self.selectExecutableAct)
351 menu.addSeparator()
352 menu.addAction(self.listPackagesAct)
353 menu.addAction(self.listUptodatePackagesAct)
354 menu.addAction(self.listOutdatedPackagesAct)
355 menu.addSeparator()
356 menu.addAction(self.installPipAct)
357 menu.addAction(self.installPackagesAct)
358 menu.addAction(self.installRequirementsAct)
359 menu.addSeparator()
360 menu.addAction(self.upgradePipAct)
361 menu.addAction(self.upgradePackagesAct)
362 menu.addSeparator()
363 menu.addAction(self.uninstallPackagesAct)
364 menu.addAction(self.uninstallRequirementsAct)
365 menu.addSeparator()
366 menu.addAction(self.generateRequirementsAct)
367 menu.addSeparator()
368 menu.addAction(self.searchPyPIAct)
369 menu.addSeparator()
370 menu.addAction(self.repairPipAct)
371 menu.addSeparator()
372 menu.addAction(self.editUserConfigAct)
373 menu.addAction(self.editVirtualenvConfigAct)
374 menu.addSeparator()
375 menu.addAction(self.pipConfigAct)
376
377 self.__menus["main"] = menu
378
379 menu.aboutToShow.connect(self.__aboutToShowMenu)
380
381 return menu
382
383 def __aboutToShowMenu(self):
384 """
385 Private slot to set the action enabled status.
386 """
387 enable = bool(self.__plugin.getPreferences("CurrentPipExecutable"))
388 for act in self.actions:
389 if act not in [self.selectExecutableAct,
390 self.installPipAct,
391 self.editUserConfigAct,
392 self.editVirtualenvConfigAct,
393 self.pipConfigAct]:
394 act.setEnabled(enable)
395
396 def getMenu(self, name):
397 """
398 Public method to get a reference to the requested menu.
399
400 @param name name of the menu (string)
401 @return reference to the menu (QMenu) or None, if no
402 menu with the given name exists
403 """
404 if name in self.__menus:
405 return self.__menus[name]
406 else:
407 return None
408
409 def getMenuNames(self):
410 """
411 Public method to get the names of all menus.
412
413 @return menu names (list of string)
414 """
415 return list(self.__menus.keys())
416
417 def __handleTearOffMenu(self, pip):
418 """
419 Private slot to handle a change of the pip executable.
420
421 @param pip path of the pip executable
422 @type str
423 """
424 if self.__menus["main"].isTearOffMenuVisible():
425 # determine, if torn off menu needs to be refreshed
426 enabled = self.listPackagesAct.isEnabled()
427 if ((bool(pip) and not enabled) or
428 (not bool(pip) and enabled)):
429 self.__menus["main"].hideTearOffMenu()
430
431 ##########################################################################
432 ## Methods below implement some utility functions
433 ##########################################################################
434
435 def runProcess(self, args, cmd=""):
436 """
437 Public method to execute the current pip with the given arguments.
438
439 The selected pip executable is called with the given arguments and
440 waited for its end.
441
442 @param args list of command line arguments (list of string)
443 @param cmd pip command to be used (string)
444 @return tuple containing a flag indicating success and the output
445 of the process (string)
446 """
447 if not cmd:
448 cmd = self.__plugin.getPreferences("CurrentPipExecutable")
449 ioEncoding = Preferences.getSystem("IOEncoding")
450
451 process = QProcess()
452 process.start(cmd, args)
453 procStarted = process.waitForStarted()
454 if procStarted:
455 finished = process.waitForFinished(30000)
456 if finished:
457 if process.exitCode() == 0:
458 output = str(process.readAllStandardOutput(), ioEncoding,
459 'replace')
460 return True, output
461 else:
462 return False, self.tr("pip exited with an error ({0}).")\
463 .format(process.exitCode())
464 else:
465 process.terminate()
466 process.waitForFinished(2000)
467 process.kill()
468 process.waitForFinished(3000)
469 return False, self.tr("pip did not finish within 30 seconds.")
470
471 return False, self.tr("pip could not be started.")
472
473 def __getUserConfig(self):
474 """
475 Private method to get the name of the user configuration file.
476
477 @return path of the user configuration file (string)
478 """
479 # Unix: ~/.config/pip/pip.conf
480 # OS X: ~/Library/Application Support/pip/pip.conf
481 # Windows: %APPDATA%\pip\pip.ini
482 # Environment: $PIP_CONFIG_FILE
483
484 try:
485 return os.environ["PIP_CONFIG_FILE"]
486 except KeyError:
487 pass
488
489 if Globals.isWindowsPlatform():
490 config = os.path.join(os.environ["APPDATA"], "pip", "pip.ini")
491 elif Globals.isMacPlatform():
492 config = os.path.expanduser(
493 "~/Library/Application Support/pip/pip.conf")
494 else:
495 config = os.path.expanduser("~/.config/pip/pip.conf")
496
497 return config
498
499 def __getVirtualenvConfig(self):
500 """
501 Private method to get the name of the virtualenv configuration file.
502
503 @return path of the virtualenv configuration file (string)
504 """
505 # Unix, OS X: $VIRTUAL_ENV/pip.conf
506 # Windows: %VIRTUAL_ENV%\pip.ini
507
508 if Globals.isWindowsPlatform():
509 pip = "pip.ini"
510 else:
511 pip = "pip.conf"
512 try:
513 virtualenv = os.environ["VIRTUAL_ENV"]
514 except KeyError:
515 # determine from pip executable file
516 virtualenv = os.path.dirname(os.path.dirname(
517 self.__plugin.getPreferences("CurrentPipExecutable")))
518
519 return os.path.join(virtualenv, pip)
520
521 ##########################################################################
522 ## Methods below implement the individual menu entries
523 ##########################################################################
524
525 def __selectPipExecutable(self):
526 """
527 Private method to select the pip executable to be used.
528 """
529 pipExecutables = sorted(self.__plugin.getPreferences("PipExecutables"))
530 if pipExecutables:
531 currentExecutable = self.__plugin.getPreferences(
532 "CurrentPipExecutable")
533 try:
534 index = pipExecutables.index(currentExecutable)
535 except ValueError:
536 index = 0
537 executable, ok = QInputDialog.getItem(
538 None,
539 self.tr("pip Executable"),
540 self.tr("Select pip Executable to be used:"),
541 pipExecutables, index, False)
542
543 if ok and executable:
544 self.__plugin.setPreferences("CurrentPipExecutable",
545 executable)
546 else:
547 res = E5MessageBox.yesNo(
548 None,
549 self.tr("pip Executable"),
550 self.tr("""No pip executables have been configured yet."""
551 """ Shall this be done now?"""),
552 yesDefault=True)
553 if res:
554 e5App().getObject("UserInterface").showPreferences("pipPage")
555
556 def __listPackages(self):
557 """
558 Private slot to list all installed packages.
559 """
560 from .PipListDialog import PipListDialog
561 self.__listDialog = PipListDialog(
562 self, "list", self.__plugin, self.tr("Installed Packages"))
563 self.__listDialog.show()
564 self.__listDialog.start()
565
566 def __listUptodatePackages(self):
567 """
568 Private slot to list all installed, up-to-date packages.
569 """
570 from .PipListDialog import PipListDialog
571 self.__listUptodateDialog = PipListDialog(
572 self, "uptodate", self.__plugin, self.tr("Up-to-date Packages"))
573 self.__listUptodateDialog.show()
574 self.__listUptodateDialog.start()
575
576 def __listOutdatedPackages(self):
577 """
578 Private slot to list all installed, up-to-date packages.
579 """
580 from .PipListDialog import PipListDialog
581 self.__listOutdatedDialog = PipListDialog(
582 self, "outdated", self.__plugin, self.tr("Outdated Packages"))
583 self.__listOutdatedDialog.show()
584 self.__listOutdatedDialog.start()
585
586 def __editUserConfiguration(self):
587 """
588 Private slot to edit the user configuration.
589 """
590 self.__editConfiguration()
591
592 def __editVirtualenvConfiguration(self):
593 """
594 Private slot to edit the current virtualenv configuration.
595 """
596 self.__editConfiguration(virtualenv=True)
597
598 def __editConfiguration(self, virtualenv=False):
599 """
600 Private method to edit a configuration.
601
602 @param virtualenv flag indicating to edit the current virtualenv
603 configuration file (boolean)
604 """
605 from QScintilla.MiniEditor import MiniEditor
606 if virtualenv:
607 cfgFile = self.__getVirtualenvConfig()
608 else:
609 cfgFile = self.__getUserConfig()
610 cfgDir = os.path.dirname(cfgFile)
611 if not cfgDir:
612 E5MessageBox.critical(
613 None,
614 self.tr("Edit Configuration"),
615 self.tr("""No valid configuartion path determined."""
616 """ Is a virtual environment selected? Aborting"""))
617 return
618
619 try:
620 if not os.path.isdir(cfgDir):
621 os.makedirs(cfgDir)
622 except OSError:
623 E5MessageBox.critical(
624 None,
625 self.tr("Edit Configuration"),
626 self.tr("""No valid configuartion path determined."""
627 """ Is a virtual environment selected? Aborting"""))
628 return
629
630 if not os.path.exists(cfgFile):
631 try:
632 f = open(cfgFile, "w")
633 f.write("[global]\n")
634 f.close()
635 except (IOError, OSError):
636 # ignore these
637 pass
638
639 # check, if the destination is writeable
640 if not os.access(cfgFile, os.W_OK):
641 E5MessageBox.critical(
642 None,
643 self.tr("Edit Configuration"),
644 self.tr("""No valid configuartion path determined."""
645 """ Is a virtual environment selected? Aborting"""))
646 return
647
648 self.__editor = MiniEditor(cfgFile, "Properties")
649 self.__editor.show()
650
651 def __installPip(self):
652 """
653 Private slot to install pip.
654 """
655 python = E5FileDialog.getOpenFileName(
656 None,
657 self.tr("Select Python Executable"))
658 if python:
659 python = QDir.toNativeSeparators(python)
660 dia = PipDialog(self.tr('Install PIP'))
661 res = dia.startProcesses([
662 (python, ["-m", "ensurepip"]),
663 (python, ["-m", "pip", "install", "--upgrade", "pip"]),
664 ])
665 if res:
666 dia.exec_()
667 pip = E5FileDialog.getOpenFileName(
668 None,
669 self.tr("Select PIP Executable"),
670 os.path.dirname(python))
671 if pip:
672 pip = QDir.toNativeSeparators(pip)
673 pipExecutables = \
674 self.__plugin.getPreferences("PipExecutables")
675 if pip not in pipExecutables:
676 pipExecutables.append(pip)
677 self.__plugin.setPreferences(
678 "PipExecutables", pipExecutables)
679
680 @pyqtSlot()
681 def upgradePip(self, pip=""):
682 """
683 Public method to upgrade pip itself.
684
685 @param pip pip command to be used
686 @type str
687 @return flag indicating a successful execution
688 @rtype bool
689 """
690 # Upgrading pip needs to be treated specially because
691 # it must be done using the python executable
692
693 if not pip:
694 default = self.tr("<Default>")
695 pipExecutables = sorted(
696 self.__plugin.getPreferences("PipExecutables"))
697 pip, ok = QInputDialog.getItem(
698 None,
699 self.tr("Upgrade pip"),
700 self.tr("Select pip Executable:"),
701 [default] + pipExecutables,
702 0, False)
703 if not ok or not pip:
704 return False
705
706 if pip == default:
707 pip = self.__plugin.getPreferences("CurrentPipExecutable")
708
709 python = self.__getPython(pip)
710 if not python:
711 python = E5FileDialog.getOpenFileName(
712 None,
713 self.tr("Select Python Executable"),
714 os.path.dirname(pip))
715 if python:
716 python = QDir.toNativeSeparators(python)
717 else:
718 return False
719
720 args = ["-m", "pip", "install", "--upgrade", "pip"]
721 dia = PipDialog(self.tr('Upgrade PIP'))
722 res = dia.startProcess(python, args)
723 if res:
724 dia.exec_()
725 return res
726
727 @pyqtSlot()
728 def __repairPip(self):
729 default = self.tr("<Default>")
730 pipExecutables = sorted(
731 self.__plugin.getPreferences("PipExecutables"))
732 pip, ok = QInputDialog.getItem(
733 None,
734 self.tr("Upgrade pip"),
735 self.tr("Select pip Executable:"),
736 [default] + pipExecutables,
737 0, False)
738 if not ok or not pip:
739 return False
740
741 if pip == default:
742 pip = self.__plugin.getPreferences("CurrentPipExecutable")
743
744 python = self.__getPython(pip)
745 if not python:
746 python = E5FileDialog.getOpenFileName(
747 None,
748 self.tr("Select Python Executable"),
749 os.path.dirname(pip))
750 if python:
751 python = QDir.toNativeSeparators(python)
752 else:
753 return False
754
755 # pip install --ignore-installed pip
756 args = ["-m", "pip", "install", "--ignore-installed", "pip"]
757 dia = PipDialog(self.tr('Repair PIP'))
758 res = dia.startProcess(python, args)
759 if res:
760 dia.exec_()
761
762 def __getPython(self, cmd):
763 """
764 Private method to derive the path to the python executable given the
765 path to the pip executable.
766
767 @param cmd path of the pip executable
768 @type str
769 @return path of the python executable
770 @rtype str
771 """
772 path, prog = os.path.split(cmd)
773 paths = (path, os.path.split(path)[0]) # to try the parent directory
774 if Globals.isWindowsPlatform():
775 subPatterns = ((r"\d\.\d", ""),
776 (r"\d\.", "."))
777 for pyname in ("python", "pypy"):
778 python = prog.replace("pip", pyname)
779 for pattern, repl in subPatterns:
780 if re.search(pattern, python):
781 python = re.sub(pattern, repl, python)
782 break
783 for path in paths:
784 pypath = os.path.join(path, python)
785 if os.path.exists(pypath):
786 return pypath
787 else:
788 subPatterns = ((r"\.\d$", ""),
789 (r"\d\.\d$", ""),
790 (r"\d$", ""))
791 for pyname in ("python", "pypy"):
792 python = prog.replace("pip", pyname)
793 for path in paths:
794 pypath = os.path.join(path, python)
795 if os.path.exists(pypath):
796 return pypath
797
798 for pattern, repl in subPatterns:
799 if re.search(pattern, cmd):
800 newpy = re.sub(pattern, repl, python)
801 pypath = os.path.join(path, newpy)
802 if os.path.exists(pypath):
803 return pypath
804
805 return ""
806
807 def __checkUpgradePyQt(self, packages):
808 """
809 Private method to check, if an upgrade of PyQt packages is attempted.
810
811 @param packages list of packages to upgrade
812 @type list of str
813 @return flag indicating to abort the upgrade attempt
814 @rtype bool
815 """
816 pyqtPackages = [p for p in packages
817 if p.lower() in ["pyqt5", "qscintilla", "sip"]]
818
819 if bool(pyqtPackages):
820 abort = not E5MessageBox.yesNo(
821 None,
822 self.tr("Upgrade Packages"),
823 self.tr(
824 """You are trying to upgrade PyQt packages. This will"""
825 """ not work for the current instance of Python ({0})."""
826 """ Do you want to continue?""").format(sys.executable),
827 icon=E5MessageBox.Critical)
828 else:
829 abort = False
830
831 return abort
832
833 def upgradePackages(self, packages, cmd=""):
834 """
835 Public method to upgrade the given list of packages.
836
837 @param packages list of packages to upgrade (list of string)
838 @param cmd pip command to be used (string)
839 @return flag indicating a successful execution (boolean)
840 """
841 if self.__checkUpgradePyQt(packages):
842 return False
843
844 if not cmd:
845 cmd = self.__plugin.getPreferences("CurrentPipExecutable")
846 args = ["install", "--upgrade"] + packages
847 dia = PipDialog(self.tr('Upgrade Packages'))
848 res = dia.startProcess(cmd, args)
849 if res:
850 dia.exec_()
851 return res
852
853 def __upgradePackages(self):
854 """
855 Private slot to upgrade packages to be given by the user.
856 """
857 from .PipPackagesInputDialog import PipPackagesInputDialog
858 dlg = PipPackagesInputDialog(
859 self.__plugin, self.tr("Upgrade Packages"))
860 if dlg.exec_() == QDialog.Accepted:
861 command, packages = dlg.getData()
862 if packages:
863 self.upgradePackages(packages, cmd=command)
864
865 def installPackages(self, packages, cmd=""):
866 """
867 Public method to install the given list of packages.
868
869 @param packages list of packages to install (list of string)
870 @param cmd pip command to be used (string)
871 """
872 if not cmd:
873 cmd = self.__plugin.getPreferences("CurrentPipExecutable")
874 args = ["install"] + packages
875 dia = PipDialog(self.tr('Install Packages'))
876 res = dia.startProcess(cmd, args)
877 if res:
878 dia.exec_()
879
880 def __installPackages(self):
881 """
882 Private slot to install packages to be given by the user.
883 """
884 from .PipPackagesInputDialog import PipPackagesInputDialog
885 dlg = PipPackagesInputDialog(
886 self.__plugin, self.tr("Install Packages"))
887 if dlg.exec_() == QDialog.Accepted:
888 command, packages = dlg.getData()
889 if packages:
890 self.installPackages(packages, cmd=command)
891
892 def __installRequirements(self):
893 """
894 Private slot to install packages as given in a requirements file.
895 """
896 from .PipRequirementsSelectionDialog import \
897 PipRequirementsSelectionDialog
898 dlg = PipRequirementsSelectionDialog(self.__plugin)
899 if dlg.exec_() == QDialog.Accepted:
900 command, requirements = dlg.getData()
901 if requirements and os.path.exists(requirements):
902 if not command:
903 command = self.__plugin.getPreferences(
904 "CurrentPipExecutable")
905 args = ["install", "--requirement", requirements]
906 dia = PipDialog(self.tr('Install Packages from Requirements'))
907 res = dia.startProcess(command, args)
908 if res:
909 dia.exec_()
910
911 def uninstallPackages(self, packages, cmd=""):
912 """
913 Public method to uninstall the given list of packages.
914
915 @param packages list of packages to uninstall (list of string)
916 @param cmd pip command to be used (string)
917 @return flag indicating a successful execution (boolean)
918 """
919 res = False
920 if packages:
921 from UI.DeleteFilesConfirmationDialog import \
922 DeleteFilesConfirmationDialog
923 dlg = DeleteFilesConfirmationDialog(
924 self.parent(),
925 self.tr("Uninstall Packages"),
926 self.tr(
927 "Do you really want to uninstall these packages?"),
928 packages)
929 if dlg.exec_() == QDialog.Accepted:
930 if not cmd:
931 cmd = self.__plugin.getPreferences("CurrentPipExecutable")
932 args = ["uninstall", "--yes"] + packages
933 dia = PipDialog(self.tr('Uninstall Packages'))
934 res = dia.startProcess(cmd, args)
935 if res:
936 dia.exec_()
937 return res
938
939 def __uninstallPackages(self):
940 """
941 Private slot to uninstall packages to be given by the user.
942 """
943 from .PipPackagesInputDialog import PipPackagesInputDialog
944 dlg = PipPackagesInputDialog(
945 self.__plugin, self.tr("Uninstall Packages"))
946 if dlg.exec_() == QDialog.Accepted:
947 command, packages = dlg.getData()
948 if packages:
949 self.uninstallPackages(packages, cmd=command)
950
951 def __uninstallRequirements(self):
952 """
953 Private slot to uninstall packages as given in a requirements file.
954 """
955 from .PipRequirementsSelectionDialog import \
956 PipRequirementsSelectionDialog
957 dlg = PipRequirementsSelectionDialog(self.__plugin)
958 if dlg.exec_() == QDialog.Accepted:
959 command, requirements = dlg.getData()
960 if requirements and os.path.exists(requirements):
961 try:
962 f = open(requirements, "r")
963 reqs = f.read().splitlines()
964 f.close()
965 except (OSError, IOError):
966 return
967
968 from UI.DeleteFilesConfirmationDialog import \
969 DeleteFilesConfirmationDialog
970 dlg = DeleteFilesConfirmationDialog(
971 self.parent(),
972 self.tr("Uninstall Packages"),
973 self.tr(
974 "Do you really want to uninstall these packages?"),
975 reqs)
976 if dlg.exec_() == QDialog.Accepted:
977 if not command:
978 command = self.__plugin.getPreferences(
979 "CurrentPipExecutable")
980 args = ["uninstall", "--requirement", requirements]
981 dia = PipDialog(
982 self.tr('Uninstall Packages from Requirements'))
983 res = dia.startProcess(command, args)
984 if res:
985 dia.exec_()
986
987 def __generateRequirements(self):
988 """
989 Private slot to generate the contents for a requirements file.
990 """
991 from .PipFreezeDialog import PipFreezeDialog
992 self.__freezeDialog = PipFreezeDialog(self, self.__plugin)
993 self.__freezeDialog.show()
994 self.__freezeDialog.start()
995
996 def __searchPyPI(self):
997 """
998 Private slot to search the Python Package Index.
999 """
1000 from .PipSearchDialog import PipSearchDialog
1001 self.__searchDialog = PipSearchDialog(self, self.__plugin)
1002 self.__searchDialog.show()
1003
1004 def __pipConfigure(self):
1005 """
1006 Private slot to open the configuration page.
1007 """
1008 e5App().getObject("UserInterface").showPreferences("pipPage")

eric ide

mercurial