PluginManager/PluginInstallDialog.py

changeset 2992
dbdf27746da5
parent 2960
9453efa25fd5
child 3034
7ce719013078
child 3057
10516539f238
equal deleted inserted replaced
2991:226481ff40d1 2992:dbdf27746da5
14 import compileall 14 import compileall
15 import urllib.parse 15 import urllib.parse
16 import glob 16 import glob
17 17
18 from PyQt4.QtCore import pyqtSlot, Qt, QDir, QFileInfo 18 from PyQt4.QtCore import pyqtSlot, Qt, QDir, QFileInfo
19 from PyQt4.QtGui import QWidget, QDialogButtonBox, QAbstractButton, QApplication, \ 19 from PyQt4.QtGui import QWidget, QDialogButtonBox, QAbstractButton, \
20 QDialog, QVBoxLayout 20 QApplication, QDialog, QVBoxLayout
21 21
22 from E5Gui import E5FileDialog 22 from E5Gui import E5FileDialog
23 from E5Gui.E5MainWindow import E5MainWindow 23 from E5Gui.E5MainWindow import E5MainWindow
24 24
25 from .Ui_PluginInstallDialog import Ui_PluginInstallDialog 25 from .Ui_PluginInstallDialog import Ui_PluginInstallDialog
53 self.__external = True 53 self.__external = True
54 else: 54 else:
55 self.__pluginManager = pluginManager 55 self.__pluginManager = pluginManager
56 self.__external = False 56 self.__external = False
57 57
58 self.__backButton = \ 58 self.__backButton = self.buttonBox.addButton(
59 self.buttonBox.addButton(self.trUtf8("< Back"), QDialogButtonBox.ActionRole) 59 self.trUtf8("< Back"), QDialogButtonBox.ActionRole)
60 self.__nextButton = \ 60 self.__nextButton = self.buttonBox.addButton(
61 self.buttonBox.addButton(self.trUtf8("Next >"), QDialogButtonBox.ActionRole) 61 self.trUtf8("Next >"), QDialogButtonBox.ActionRole)
62 self.__finishButton = \ 62 self.__finishButton = self.buttonBox.addButton(
63 self.buttonBox.addButton(self.trUtf8("Install"), QDialogButtonBox.ActionRole) 63 self.trUtf8("Install"), QDialogButtonBox.ActionRole)
64 64
65 self.__closeButton = self.buttonBox.button(QDialogButtonBox.Close) 65 self.__closeButton = self.buttonBox.button(QDialogButtonBox.Close)
66 self.__cancelButton = self.buttonBox.button(QDialogButtonBox.Cancel) 66 self.__cancelButton = self.buttonBox.button(QDialogButtonBox.Cancel)
67 67
68 userDir = self.__pluginManager.getPluginDir("user") 68 userDir = self.__pluginManager.getPluginDir("user")
69 if userDir is not None: 69 if userDir is not None:
70 self.destinationCombo.addItem(self.trUtf8("User plugins directory"), 70 self.destinationCombo.addItem(
71 self.trUtf8("User plugins directory"),
71 userDir) 72 userDir)
72 73
73 globalDir = self.__pluginManager.getPluginDir("global") 74 globalDir = self.__pluginManager.getPluginDir("global")
74 if globalDir is not None and os.access(globalDir, os.W_OK): 75 if globalDir is not None and os.access(globalDir, os.W_OK):
75 self.destinationCombo.addItem(self.trUtf8("Global plugins directory"), 76 self.destinationCombo.addItem(
77 self.trUtf8("Global plugins directory"),
76 globalDir) 78 globalDir)
77 79
78 self.__installedDirs = [] 80 self.__installedDirs = []
79 self.__installedFiles = [] 81 self.__installedFiles = []
80 82
82 84
83 downloadDir = QDir(Preferences.getPluginManager("DownloadPath")) 85 downloadDir = QDir(Preferences.getPluginManager("DownloadPath"))
84 for pluginFileName in pluginFileNames: 86 for pluginFileName in pluginFileNames:
85 fi = QFileInfo(pluginFileName) 87 fi = QFileInfo(pluginFileName)
86 if fi.isRelative(): 88 if fi.isRelative():
87 pluginFileName = QFileInfo(downloadDir, fi.fileName()).absoluteFilePath() 89 pluginFileName = QFileInfo(
90 downloadDir, fi.fileName()).absoluteFilePath()
88 self.archivesList.addItem(pluginFileName) 91 self.archivesList.addItem(pluginFileName)
89 self.archivesList.sortItems() 92 self.archivesList.sortItems()
90 93
91 self.__currentIndex = 0 94 self.__currentIndex = 0
92 self.__selectPage() 95 self.__selectPage()
132 self.__nextButton.setEnabled(False) 135 self.__nextButton.setEnabled(False)
133 self.__finishButton.setEnabled(True) 136 self.__finishButton.setEnabled(True)
134 self.__closeButton.hide() 137 self.__closeButton.hide()
135 self.__cancelButton.show() 138 self.__cancelButton.show()
136 139
137 msg = self.trUtf8("Plugin ZIP-Archives:\n{0}\n\nDestination:\n{1} ({2})")\ 140 msg = self.trUtf8(
141 "Plugin ZIP-Archives:\n{0}\n\nDestination:\n{1} ({2})")\
138 .format("\n".join(self.__createArchivesList()), 142 .format("\n".join(self.__createArchivesList()),
139 self.destinationCombo.currentText(), 143 self.destinationCombo.currentText(),
140 self.destinationCombo.itemData( 144 self.destinationCombo.itemData(
141 self.destinationCombo.currentIndex()) 145 self.destinationCombo.currentIndex())
142 ) 146 )
169 @pyqtSlot() 173 @pyqtSlot()
170 def on_archivesList_itemSelectionChanged(self): 174 def on_archivesList_itemSelectionChanged(self):
171 """ 175 """
172 Private slot called, when the selection of the archives list changes. 176 Private slot called, when the selection of the archives list changes.
173 """ 177 """
174 self.removeArchivesButton.setEnabled(len(self.archivesList.selectedItems()) > 0) 178 self.removeArchivesButton.setEnabled(
179 len(self.archivesList.selectedItems()) > 0)
175 180
176 @pyqtSlot() 181 @pyqtSlot()
177 def on_removeArchivesButton_clicked(self): 182 def on_removeArchivesButton_clicked(self):
178 """ 183 """
179 Private slot to remove archives from the list. 184 Private slot to remove archives from the list.
180 """ 185 """
181 for archiveItem in self.archivesList.selectedItems(): 186 for archiveItem in self.archivesList.selectedItems():
182 itm = self.archivesList.takeItem(self.archivesList.row(archiveItem)) 187 itm = self.archivesList.takeItem(
188 self.archivesList.row(archiveItem))
183 del itm 189 del itm
184 190
185 self.__nextButton.setEnabled(self.archivesList.count() > 0) 191 self.__nextButton.setEnabled(self.archivesList.count() > 0)
186 192
187 @pyqtSlot(QAbstractButton) 193 @pyqtSlot(QAbstractButton)
210 @return flag indicating success (boolean) 216 @return flag indicating success (boolean)
211 """ 217 """
212 res = True 218 res = True
213 self.summaryEdit.clear() 219 self.summaryEdit.clear()
214 for archive in self.__createArchivesList(): 220 for archive in self.__createArchivesList():
215 self.summaryEdit.append(self.trUtf8("Installing {0} ...").format(archive)) 221 self.summaryEdit.append(
222 self.trUtf8("Installing {0} ...").format(archive))
216 ok, msg, restart = self.__installPlugin(archive) 223 ok, msg, restart = self.__installPlugin(archive)
217 res = res and ok 224 res = res and ok
218 if ok: 225 if ok:
219 self.summaryEdit.append(self.trUtf8(" ok")) 226 self.summaryEdit.append(self.trUtf8(" ok"))
220 else: 227 else:
242 of the IDE is required (boolean) 249 of the IDE is required (boolean)
243 """ 250 """
244 installedPluginName = "" 251 installedPluginName = ""
245 252
246 archive = archiveFilename 253 archive = archiveFilename
247 destination = \ 254 destination = self.destinationCombo.itemData(
248 self.destinationCombo.itemData(self.destinationCombo.currentIndex()) 255 self.destinationCombo.currentIndex())
249 256
250 # check if archive is a local url 257 # check if archive is a local url
251 url = urllib.parse.urlparse(archive) 258 url = urllib.parse.urlparse(archive)
252 if url[0].lower() == 'file': 259 if url[0].lower() == 'file':
253 archive = url[2] 260 archive = url[2]
254 261
255 # check, if the archive exists 262 # check, if the archive exists
256 if not os.path.exists(archive): 263 if not os.path.exists(archive):
257 return False, \ 264 return False, \
258 self.trUtf8("""<p>The archive file <b>{0}</b> does not exist. """ 265 self.trUtf8(
259 """Aborting...</p>""").format(archive), \ 266 """<p>The archive file <b>{0}</b> does not exist. """
267 """Aborting...</p>""").format(archive), \
260 False 268 False
261 269
262 # check, if the archive is a valid zip file 270 # check, if the archive is a valid zip file
263 if not zipfile.is_zipfile(archive): 271 if not zipfile.is_zipfile(archive):
264 return False, \ 272 return False, \
265 self.trUtf8("""<p>The file <b>{0}</b> is not a valid plugin """ 273 self.trUtf8(
266 """ZIP-archive. Aborting...</p>""").format(archive), \ 274 """<p>The file <b>{0}</b> is not a valid plugin """
275 """ZIP-archive. Aborting...</p>""").format(archive), \
267 False 276 False
268 277
269 # check, if the destination is writeable 278 # check, if the destination is writeable
270 if not os.access(destination, os.W_OK): 279 if not os.access(destination, os.W_OK):
271 return False, \ 280 return False, \
272 self.trUtf8("""<p>The destination directory <b>{0}</b> is not """ 281 self.trUtf8(
273 """writeable. Aborting...</p>""").format(destination), \ 282 """<p>The destination directory <b>{0}</b> is not """
283 """writeable. Aborting...</p>""").format(destination), \
274 False 284 False
275 285
276 zip = zipfile.ZipFile(archive, "r") 286 zip = zipfile.ZipFile(archive, "r")
277 287
278 # check, if the archive contains a valid plugin 288 # check, if the archive contains a valid plugin
285 pluginFileName = name 295 pluginFileName = name
286 break 296 break
287 297
288 if not pluginFound: 298 if not pluginFound:
289 return False, \ 299 return False, \
290 self.trUtf8("""<p>The file <b>{0}</b> is not a valid plugin """ 300 self.trUtf8(
291 """ZIP-archive. Aborting...</p>""").format(archive), \ 301 """<p>The file <b>{0}</b> is not a valid plugin """
302 """ZIP-archive. Aborting...</p>""").format(archive), \
292 False 303 False
293 304
294 # parse the plugin module's plugin header 305 # parse the plugin module's plugin header
295 pluginSource = Utilities.decode(zip.read(pluginFileName))[0] 306 pluginSource = Utilities.decode(zip.read(pluginFileName))[0]
296 packageName = "" 307 packageName = ""
308 else: 319 else:
309 if tokens[1].strip() == "None": 320 if tokens[1].strip() == "None":
310 packageName = "None" 321 packageName = "None"
311 elif line.startswith("internalPackages"): 322 elif line.startswith("internalPackages"):
312 tokens = line.split("=") 323 tokens = line.split("=")
313 token = tokens[1].strip()[1:-1] # it is a comma separated string 324 token = tokens[1].strip()[1:-1]
325 # it is a comma separated string
314 internalPackages = [p.strip() for p in token.split(",")] 326 internalPackages = [p.strip() for p in token.split(",")]
315 elif line.startswith("needsRestart"): 327 elif line.startswith("needsRestart"):
316 tokens = line.split("=") 328 tokens = line.split("=")
317 needsRestart = tokens[1].strip() == "True" 329 needsRestart = tokens[1].strip() == "True"
318 elif line.startswith("pyqtApi"): 330 elif line.startswith("pyqtApi"):
328 elif line.startswith("# End-Of-Header"): 340 elif line.startswith("# End-Of-Header"):
329 break 341 break
330 342
331 if not packageName: 343 if not packageName:
332 return False, \ 344 return False, \
333 self.trUtf8("""<p>The plugin module <b>{0}</b> does not contain """ 345 self.trUtf8(
334 """a 'packageName' attribute. Aborting...</p>""")\ 346 """<p>The plugin module <b>{0}</b> does not contain """
347 """a 'packageName' attribute. Aborting...</p>""")\
335 .format(pluginFileName), \ 348 .format(pluginFileName), \
336 False 349 False
337 350
338 if pyqtApi < 2: 351 if pyqtApi < 2:
339 return False, \ 352 return False, \
340 self.trUtf8("""<p>The plugin module <b>{0}</b> does not conform""" 353 self.trUtf8(
341 """ with the PyQt v2 API. Aborting...</p>""")\ 354 """<p>The plugin module <b>{0}</b> does not conform"""
355 """ with the PyQt v2 API. Aborting...</p>""")\
342 .format(pluginFileName), \ 356 .format(pluginFileName), \
343 False 357 False
344 358
345 # check, if it is a plugin, that collides with others 359 # check, if it is a plugin, that collides with others
346 if not os.path.exists(os.path.join(destination, pluginFileName)) and \ 360 if not os.path.exists(os.path.join(destination, pluginFileName)) and \
362 False 376 False
363 377
364 activatePlugin = False 378 activatePlugin = False
365 if not self.__external: 379 if not self.__external:
366 activatePlugin = \ 380 activatePlugin = \
367 not self.__pluginManager.isPluginLoaded(installedPluginName) or \ 381 not self.__pluginManager.isPluginLoaded(
382 installedPluginName) or \
368 (self.__pluginManager.isPluginLoaded(installedPluginName) and \ 383 (self.__pluginManager.isPluginLoaded(installedPluginName) and \
369 self.__pluginManager.isPluginActive(installedPluginName)) 384 self.__pluginManager.isPluginActive(installedPluginName))
370 # try to unload a plugin with the same name 385 # try to unload a plugin with the same name
371 self.__pluginManager.unloadPlugin(installedPluginName) 386 self.__pluginManager.unloadPlugin(installedPluginName)
372 387
420 f.close() 435 f.close()
421 self.__installedFiles.append(outname) 436 self.__installedFiles.append(outname)
422 except os.error as why: 437 except os.error as why:
423 self.__rollback() 438 self.__rollback()
424 return False, \ 439 return False, \
425 self.trUtf8("Error installing plugin. Reason: {0}").format(str(why)), \ 440 self.trUtf8(
441 "Error installing plugin. Reason: {0}").format(str(why)), \
426 False 442 False
427 except IOError as why: 443 except IOError as why:
428 self.__rollback() 444 self.__rollback()
429 return False, \ 445 return False, \
430 self.trUtf8("Error installing plugin. Reason: {0}").format(str(why)), \ 446 self.trUtf8(
447 "Error installing plugin. Reason: {0}").format(str(why)), \
431 False 448 False
432 except OSError as why: 449 except OSError as why:
433 self.__rollback() 450 self.__rollback()
434 return False, \ 451 return False, \
435 self.trUtf8("Error installing plugin. Reason: {0}").format(str(why)), \ 452 self.trUtf8(
453 "Error installing plugin. Reason: {0}").format(str(why)), \
436 False 454 False
437 except: 455 except:
438 print("Unspecific exception installing plugin.", file=sys.stderr) 456 print("Unspecific exception installing plugin.", file=sys.stderr)
439 self.__rollback() 457 self.__rollback()
440 return False, \ 458 return False, \
441 self.trUtf8("Unspecific exception installing plugin."), \ 459 self.trUtf8("Unspecific exception installing plugin."), \
442 False 460 False
443 461
444 # now compile the plugins 462 # now compile the plugins
445 if doCompile: 463 if doCompile:
446 compileall.compile_dir(os.path.join(destination, packageName), quiet=True) 464 compileall.compile_dir(
447 compileall.compile_file(os.path.join(destination, pluginFileName), quiet=True) 465 os.path.join(destination, packageName), quiet=True)
466 compileall.compile_file(
467 os.path.join(destination, pluginFileName), quiet=True)
448 468
449 if not self.__external: 469 if not self.__external:
450 # now load and activate the plugin 470 # now load and activate the plugin
451 self.__pluginManager.loadPlugin(installedPluginName, destination, reload_) 471 self.__pluginManager.loadPlugin(installedPluginName, destination,
472 reload_)
452 if activatePlugin: 473 if activatePlugin:
453 self.__pluginManager.activatePlugin(installedPluginName) 474 self.__pluginManager.activatePlugin(installedPluginName)
454 475
455 return True, "", needsRestart 476 return True, "", needsRestart
456 477
478 head, tail = os.path.split(name) 499 head, tail = os.path.split(name)
479 if not tail: 500 if not tail:
480 head, tail = os.path.split(head) 501 head, tail = os.path.split(head)
481 if head and tail and not os.path.exists(head): 502 if head and tail and not os.path.exists(head):
482 self.__makedirs(head, mode) 503 self.__makedirs(head, mode)
483 if tail == os.curdir: # xxx/newdir/. exists if xxx/newdir exists 504 if tail == os.curdir:
505 # xxx/newdir/. exists if xxx/newdir exists
484 return 506 return
485 os.mkdir(name, mode) 507 os.mkdir(name, mode)
486 self.__installedDirs.append(name) 508 self.__installedDirs.append(name)
487 509
488 def __uninstallPackage(self, destination, pluginFileName, packageName): 510 def __uninstallPackage(self, destination, pluginFileName, packageName):
510 532
511 fnamec = "{0}c".format(pluginFile) 533 fnamec = "{0}c".format(pluginFile)
512 if os.path.exists(fnamec): 534 if os.path.exists(fnamec):
513 os.remove(fnamec) 535 os.remove(fnamec)
514 536
515 pluginDirCache = os.path.join(os.path.dirname(pluginFile), "__pycache__") 537 pluginDirCache = os.path.join(
538 os.path.dirname(pluginFile), "__pycache__")
516 if os.path.exists(pluginDirCache): 539 if os.path.exists(pluginDirCache):
517 pluginFileName = os.path.splitext(os.path.basename(pluginFile))[0] 540 pluginFileName = os.path.splitext(
541 os.path.basename(pluginFile))[0]
518 for fnameo in glob.glob( 542 for fnameo in glob.glob(
519 os.path.join(pluginDirCache, "{0}*.pyo".format(pluginFileName))): 543 os.path.join(pluginDirCache,
544 "{0}*.pyo".format(pluginFileName))):
520 os.remove(fnameo) 545 os.remove(fnameo)
521 for fnamec in glob.glob( 546 for fnamec in glob.glob(
522 os.path.join(pluginDirCache, "{0}*.pyc".format(pluginFileName))): 547 os.path.join(pluginDirCache,
548 "{0}*.pyc".format(pluginFileName))):
523 os.remove(fnamec) 549 os.remove(fnamec)
524 550
525 os.remove(pluginFile) 551 os.remove(pluginFile)
526 except (IOError, OSError, os.error): 552 except (IOError, OSError, os.error):
527 # ignore some exceptions 553 # ignore some exceptions
581 self.cw = PluginInstallWidget(None, pluginFileNames, self) 607 self.cw = PluginInstallWidget(None, pluginFileNames, self)
582 size = self.cw.size() 608 size = self.cw.size()
583 self.setCentralWidget(self.cw) 609 self.setCentralWidget(self.cw)
584 self.resize(size) 610 self.resize(size)
585 611
586 self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) 612 self.setStyle(Preferences.getUI("Style"),
613 Preferences.getUI("StyleSheet"))
587 614
588 self.cw.buttonBox.accepted[()].connect(self.close) 615 self.cw.buttonBox.accepted[()].connect(self.close)
589 self.cw.buttonBox.rejected[()].connect(self.close) 616 self.cw.buttonBox.rejected[()].connect(self.close)

eric ide

mercurial