39 |
37 |
40 class Pip(QObject): |
38 class Pip(QObject): |
41 """ |
39 """ |
42 Class implementing the pip GUI logic. |
40 Class implementing the pip GUI logic. |
43 """ |
41 """ |
|
42 |
44 DefaultPyPiUrl = "https://pypi.org" |
43 DefaultPyPiUrl = "https://pypi.org" |
45 DefaultIndexUrlPypi = DefaultPyPiUrl + "/pypi" |
44 DefaultIndexUrlPypi = DefaultPyPiUrl + "/pypi" |
46 DefaultIndexUrlSimple = DefaultPyPiUrl + "/simple" |
45 DefaultIndexUrlSimple = DefaultPyPiUrl + "/simple" |
47 DefaultIndexUrlSearch = DefaultPyPiUrl + "/search/" |
46 DefaultIndexUrlSearch = DefaultPyPiUrl + "/search/" |
48 |
47 |
49 def __init__(self, parent=None): |
48 def __init__(self, parent=None): |
50 """ |
49 """ |
51 Constructor |
50 Constructor |
52 |
51 |
53 @param parent reference to the user interface object |
52 @param parent reference to the user interface object |
54 @type QObject |
53 @type QObject |
55 """ |
54 """ |
56 super().__init__(parent) |
55 super().__init__(parent) |
57 |
56 |
58 self.__ui = parent |
57 self.__ui = parent |
59 |
58 |
60 # attributes for the network objects |
59 # attributes for the network objects |
61 self.__networkManager = QNetworkAccessManager(self) |
60 self.__networkManager = QNetworkAccessManager(self) |
62 self.__networkManager.proxyAuthenticationRequired.connect( |
61 self.__networkManager.proxyAuthenticationRequired.connect( |
63 proxyAuthenticationRequired) |
62 proxyAuthenticationRequired |
|
63 ) |
64 if SSL_AVAILABLE: |
64 if SSL_AVAILABLE: |
65 self.__sslErrorHandler = EricSslErrorHandler(self) |
65 self.__sslErrorHandler = EricSslErrorHandler(self) |
66 self.__networkManager.sslErrors.connect( |
66 self.__networkManager.sslErrors.connect( |
67 self.__sslErrorHandler.sslErrorsReply) |
67 self.__sslErrorHandler.sslErrorsReply |
|
68 ) |
68 self.__replies = [] |
69 self.__replies = [] |
69 |
70 |
70 self.__vulnerabilityChecker = PipVulnerabilityChecker(self, self) |
71 self.__vulnerabilityChecker = PipVulnerabilityChecker(self, self) |
71 |
72 |
72 def getNetworkAccessManager(self): |
73 def getNetworkAccessManager(self): |
73 """ |
74 """ |
74 Public method to get a reference to the network access manager object. |
75 Public method to get a reference to the network access manager object. |
75 |
76 |
76 @return reference to the network access manager object |
77 @return reference to the network access manager object |
77 @rtype QNetworkAccessManager |
78 @rtype QNetworkAccessManager |
78 """ |
79 """ |
79 return self.__networkManager |
80 return self.__networkManager |
80 |
81 |
81 def getVulnerabilityChecker(self): |
82 def getVulnerabilityChecker(self): |
82 """ |
83 """ |
83 Public method to get a reference to the vulnerability checker object. |
84 Public method to get a reference to the vulnerability checker object. |
84 |
85 |
85 @return reference to the vulnerability checker object |
86 @return reference to the vulnerability checker object |
86 @rtype PipVulnerabilityChecker |
87 @rtype PipVulnerabilityChecker |
87 """ |
88 """ |
88 return self.__vulnerabilityChecker |
89 return self.__vulnerabilityChecker |
89 |
90 |
90 ########################################################################## |
91 ########################################################################## |
91 ## Methods below implement some utility functions |
92 ## Methods below implement some utility functions |
92 ########################################################################## |
93 ########################################################################## |
93 |
94 |
94 def runProcess(self, args, interpreter): |
95 def runProcess(self, args, interpreter): |
95 """ |
96 """ |
96 Public method to execute the current pip with the given arguments. |
97 Public method to execute the current pip with the given arguments. |
97 |
98 |
98 The selected pip executable is called with the given arguments and |
99 The selected pip executable is called with the given arguments and |
99 waited for its end. |
100 waited for its end. |
100 |
101 |
101 @param args list of command line arguments |
102 @param args list of command line arguments |
102 @type list of str |
103 @type list of str |
103 @param interpreter path of the Python interpreter to be used |
104 @param interpreter path of the Python interpreter to be used |
104 @type str |
105 @type str |
105 @return tuple containing a flag indicating success and the output |
106 @return tuple containing a flag indicating success and the output |
106 of the process |
107 of the process |
107 @rtype tuple of (bool, str) |
108 @rtype tuple of (bool, str) |
108 """ |
109 """ |
109 ioEncoding = Preferences.getSystem("IOEncoding") |
110 ioEncoding = Preferences.getSystem("IOEncoding") |
110 |
111 |
111 process = QProcess() |
112 process = QProcess() |
112 process.start(interpreter, args) |
113 process.start(interpreter, args) |
113 procStarted = process.waitForStarted() |
114 procStarted = process.waitForStarted() |
114 if procStarted: |
115 if procStarted: |
115 finished = process.waitForFinished(30000) |
116 finished = process.waitForFinished(30000) |
116 if finished: |
117 if finished: |
117 if process.exitCode() == 0: |
118 if process.exitCode() == 0: |
118 output = str(process.readAllStandardOutput(), ioEncoding, |
119 output = str(process.readAllStandardOutput(), ioEncoding, "replace") |
119 'replace') |
|
120 return True, output |
120 return True, output |
121 else: |
121 else: |
122 return (False, |
122 return ( |
123 self.tr("python exited with an error ({0}).") |
123 False, |
124 .format(process.exitCode())) |
124 self.tr("python exited with an error ({0}).").format( |
|
125 process.exitCode() |
|
126 ), |
|
127 ) |
125 else: |
128 else: |
126 process.terminate() |
129 process.terminate() |
127 process.waitForFinished(2000) |
130 process.waitForFinished(2000) |
128 process.kill() |
131 process.kill() |
129 process.waitForFinished(3000) |
132 process.waitForFinished(3000) |
130 return False, self.tr("python did not finish within" |
133 return False, self.tr("python did not finish within" " 30 seconds.") |
131 " 30 seconds.") |
134 |
132 |
|
133 return False, self.tr("python could not be started.") |
135 return False, self.tr("python could not be started.") |
134 |
136 |
135 def getUserConfig(self): |
137 def getUserConfig(self): |
136 """ |
138 """ |
137 Public method to get the name of the user configuration file. |
139 Public method to get the name of the user configuration file. |
138 |
140 |
139 @return path of the user configuration file |
141 @return path of the user configuration file |
140 @rtype str |
142 @rtype str |
141 """ |
143 """ |
142 # Unix: ~/.config/pip/pip.conf |
144 # Unix: ~/.config/pip/pip.conf |
143 # OS X: ~/Library/Application Support/pip/pip.conf |
145 # OS X: ~/Library/Application Support/pip/pip.conf |
144 # Windows: %APPDATA%\pip\pip.ini |
146 # Windows: %APPDATA%\pip\pip.ini |
145 # Environment: $PIP_CONFIG_FILE |
147 # Environment: $PIP_CONFIG_FILE |
146 |
148 |
147 with contextlib.suppress(KeyError): |
149 with contextlib.suppress(KeyError): |
148 return os.environ["PIP_CONFIG_FILE"] |
150 return os.environ["PIP_CONFIG_FILE"] |
149 |
151 |
150 if Globals.isWindowsPlatform(): |
152 if Globals.isWindowsPlatform(): |
151 config = os.path.join(os.environ["APPDATA"], "pip", "pip.ini") |
153 config = os.path.join(os.environ["APPDATA"], "pip", "pip.ini") |
152 elif Globals.isMacPlatform(): |
154 elif Globals.isMacPlatform(): |
153 config = os.path.expanduser( |
155 config = os.path.expanduser("~/Library/Application Support/pip/pip.conf") |
154 "~/Library/Application Support/pip/pip.conf") |
|
155 else: |
156 else: |
156 config = os.path.expanduser("~/.config/pip/pip.conf") |
157 config = os.path.expanduser("~/.config/pip/pip.conf") |
157 |
158 |
158 return config |
159 return config |
159 |
160 |
160 def getVirtualenvConfig(self, venvName): |
161 def getVirtualenvConfig(self, venvName): |
161 """ |
162 """ |
162 Public method to get the name of the virtualenv configuration file. |
163 Public method to get the name of the virtualenv configuration file. |
163 |
164 |
164 @param venvName name of the environment to get config file path for |
165 @param venvName name of the environment to get config file path for |
165 @type str |
166 @type str |
166 @return path of the virtualenv configuration file |
167 @return path of the virtualenv configuration file |
167 @rtype str |
168 @rtype str |
168 """ |
169 """ |
169 # Unix, OS X: $VIRTUAL_ENV/pip.conf |
170 # Unix, OS X: $VIRTUAL_ENV/pip.conf |
170 # Windows: %VIRTUAL_ENV%\pip.ini |
171 # Windows: %VIRTUAL_ENV%\pip.ini |
171 |
172 |
172 pip = "pip.ini" if Globals.isWindowsPlatform() else "pip.conf" |
173 pip = "pip.ini" if Globals.isWindowsPlatform() else "pip.conf" |
173 |
174 |
174 venvManager = ericApp().getObject("VirtualEnvManager") |
175 venvManager = ericApp().getObject("VirtualEnvManager") |
175 venvDirectory = ( |
176 venvDirectory = ( |
176 os.path.dirname(self.getUserConfig()) |
177 os.path.dirname(self.getUserConfig()) |
177 if venvManager.isGlobalEnvironment(venvName) else |
178 if venvManager.isGlobalEnvironment(venvName) |
178 venvManager.getVirtualenvDirectory(venvName) |
179 else venvManager.getVirtualenvDirectory(venvName) |
179 ) |
180 ) |
180 |
181 |
181 config = os.path.join(venvDirectory, pip) if venvDirectory else "" |
182 config = os.path.join(venvDirectory, pip) if venvDirectory else "" |
182 |
183 |
183 return config |
184 return config |
184 |
185 |
185 def getProjectEnvironmentString(self): |
186 def getProjectEnvironmentString(self): |
186 """ |
187 """ |
187 Public method to get the string for the project environment. |
188 Public method to get the string for the project environment. |
188 |
189 |
189 @return string for the project environment |
190 @return string for the project environment |
190 @rtype str |
191 @rtype str |
191 """ |
192 """ |
192 if ericApp().getObject("Project").isOpen(): |
193 if ericApp().getObject("Project").isOpen(): |
193 return self.tr("<project>") |
194 return self.tr("<project>") |
194 else: |
195 else: |
195 return "" |
196 return "" |
196 |
197 |
197 def getVirtualenvInterpreter(self, venvName): |
198 def getVirtualenvInterpreter(self, venvName): |
198 """ |
199 """ |
199 Public method to get the interpreter for a virtual environment. |
200 Public method to get the interpreter for a virtual environment. |
200 |
201 |
201 @param venvName logical name for the virtual environment |
202 @param venvName logical name for the virtual environment |
202 @type str |
203 @type str |
203 @return interpreter path |
204 @return interpreter path |
204 @rtype str |
205 @rtype str |
205 """ |
206 """ |
206 interpreter = ( |
207 interpreter = ( |
207 ericApp().getObject("Project").getProjectInterpreter() |
208 ericApp().getObject("Project").getProjectInterpreter() |
208 if venvName == self.getProjectEnvironmentString() else |
209 if venvName == self.getProjectEnvironmentString() |
209 ericApp().getObject("VirtualEnvManager") |
210 else ericApp() |
|
211 .getObject("VirtualEnvManager") |
210 .getVirtualenvInterpreter(venvName) |
212 .getVirtualenvInterpreter(venvName) |
211 ) |
213 ) |
212 if not interpreter: |
214 if not interpreter: |
213 EricMessageBox.critical( |
215 EricMessageBox.critical( |
214 None, |
216 None, |
215 self.tr("Interpreter for Virtual Environment"), |
217 self.tr("Interpreter for Virtual Environment"), |
216 self.tr("""No interpreter configured for the selected""" |
218 self.tr( |
217 """ virtual environment.""")) |
219 """No interpreter configured for the selected""" |
218 |
220 """ virtual environment.""" |
|
221 ), |
|
222 ) |
|
223 |
219 return interpreter |
224 return interpreter |
220 |
225 |
221 def getVirtualenvNames(self, noRemote=False, noConda=False): |
226 def getVirtualenvNames(self, noRemote=False, noConda=False): |
222 """ |
227 """ |
223 Public method to get a sorted list of virtual environment names. |
228 Public method to get a sorted list of virtual environment names. |
224 |
229 |
225 @param noRemote flag indicating to exclude environments for remote |
230 @param noRemote flag indicating to exclude environments for remote |
226 debugging |
231 debugging |
227 @type bool |
232 @type bool |
228 @param noConda flag indicating to exclude Conda environments |
233 @param noConda flag indicating to exclude Conda environments |
229 @type bool |
234 @type bool |
230 @return sorted list of virtual environment names |
235 @return sorted list of virtual environment names |
231 @rtype list of str |
236 @rtype list of str |
232 """ |
237 """ |
233 return sorted( |
238 return sorted( |
234 ericApp().getObject("VirtualEnvManager").getVirtualenvNames( |
239 ericApp() |
235 noRemote=noRemote, noConda=noConda)) |
240 .getObject("VirtualEnvManager") |
236 |
241 .getVirtualenvNames(noRemote=noRemote, noConda=noConda) |
|
242 ) |
|
243 |
237 def installPip(self, venvName, userSite=False): |
244 def installPip(self, venvName, userSite=False): |
238 """ |
245 """ |
239 Public method to install pip. |
246 Public method to install pip. |
240 |
247 |
241 @param venvName name of the environment to install pip into |
248 @param venvName name of the environment to install pip into |
242 @type str |
249 @type str |
243 @param userSite flag indicating an install to the user install |
250 @param userSite flag indicating an install to the user install |
244 directory |
251 directory |
245 @type bool |
252 @type bool |
246 """ |
253 """ |
247 interpreter = self.getVirtualenvInterpreter(venvName) |
254 interpreter = self.getVirtualenvInterpreter(venvName) |
248 if not interpreter: |
255 if not interpreter: |
249 return |
256 return |
250 |
257 |
251 dia = PipDialog(self.tr('Install PIP')) |
258 dia = PipDialog(self.tr("Install PIP")) |
252 commands = ( |
259 commands = ( |
253 [(interpreter, ["-m", "ensurepip", "--user"])] |
260 [(interpreter, ["-m", "ensurepip", "--user"])] |
254 if userSite else |
261 if userSite |
255 [(interpreter, ["-m", "ensurepip"])] |
262 else [(interpreter, ["-m", "ensurepip"])] |
256 ) |
263 ) |
257 if Preferences.getPip("PipSearchIndex"): |
264 if Preferences.getPip("PipSearchIndex"): |
258 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
265 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
259 args = ["-m", "pip", "install", "--index-url", indexUrl, |
266 args = ["-m", "pip", "install", "--index-url", indexUrl, "--upgrade"] |
260 "--upgrade"] |
|
261 else: |
267 else: |
262 args = ["-m", "pip", "install", "--upgrade"] |
268 args = ["-m", "pip", "install", "--upgrade"] |
263 if userSite: |
269 if userSite: |
264 args.append("--user") |
270 args.append("--user") |
265 args.append("pip") |
271 args.append("pip") |
266 commands.append((interpreter, args[:])) |
272 commands.append((interpreter, args[:])) |
267 |
273 |
268 res = dia.startProcesses(commands) |
274 res = dia.startProcesses(commands) |
269 if res: |
275 if res: |
270 dia.exec() |
276 dia.exec() |
271 |
277 |
272 @pyqtSlot() |
278 @pyqtSlot() |
273 def repairPip(self, venvName): |
279 def repairPip(self, venvName): |
274 """ |
280 """ |
275 Public method to repair the pip installation. |
281 Public method to repair the pip installation. |
276 |
282 |
277 @param venvName name of the environment to install pip into |
283 @param venvName name of the environment to install pip into |
278 @type str |
284 @type str |
279 """ |
285 """ |
280 interpreter = self.getVirtualenvInterpreter(venvName) |
286 interpreter = self.getVirtualenvInterpreter(venvName) |
281 if not interpreter: |
287 if not interpreter: |
282 return |
288 return |
283 |
289 |
284 # python -m pip install --ignore-installed pip |
290 # python -m pip install --ignore-installed pip |
285 if Preferences.getPip("PipSearchIndex"): |
291 if Preferences.getPip("PipSearchIndex"): |
286 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
292 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
287 args = ["-m", "pip", "install", "--index-url", indexUrl, |
293 args = [ |
288 "--ignore-installed"] |
294 "-m", |
|
295 "pip", |
|
296 "install", |
|
297 "--index-url", |
|
298 indexUrl, |
|
299 "--ignore-installed", |
|
300 ] |
289 else: |
301 else: |
290 args = ["-m", "pip", "install", "--ignore-installed"] |
302 args = ["-m", "pip", "install", "--ignore-installed"] |
291 args.append("pip") |
303 args.append("pip") |
292 |
304 |
293 dia = PipDialog(self.tr('Repair PIP')) |
305 dia = PipDialog(self.tr("Repair PIP")) |
294 res = dia.startProcess(interpreter, args) |
306 res = dia.startProcess(interpreter, args) |
295 if res: |
307 if res: |
296 dia.exec() |
308 dia.exec() |
297 |
309 |
298 def __checkUpgradePyQt(self, packages): |
310 def __checkUpgradePyQt(self, packages): |
299 """ |
311 """ |
300 Private method to check, if an upgrade of PyQt packages is attempted. |
312 Private method to check, if an upgrade of PyQt packages is attempted. |
301 |
313 |
302 @param packages list of packages to upgrade |
314 @param packages list of packages to upgrade |
303 @type list of str |
315 @type list of str |
304 @return flag indicating a PyQt upgrade |
316 @return flag indicating a PyQt upgrade |
305 @rtype bool |
317 @rtype bool |
306 """ |
318 """ |
307 pyqtPackages = [ |
319 pyqtPackages = [ |
308 p for p in packages if p.lower() in [ |
320 p |
309 "pyqt6", "pyqt6-sip", "pyqt6-webengine", "pyqt6-charts", |
321 for p in packages |
310 "pyqt6-qscintilla", "pyqt6-qt6", "pyqt6-webengine-qt6", |
322 if p.lower() |
311 "pyqt6-charts-qt6" |
323 in [ |
|
324 "pyqt6", |
|
325 "pyqt6-sip", |
|
326 "pyqt6-webengine", |
|
327 "pyqt6-charts", |
|
328 "pyqt6-qscintilla", |
|
329 "pyqt6-qt6", |
|
330 "pyqt6-webengine-qt6", |
|
331 "pyqt6-charts-qt6", |
312 ] |
332 ] |
313 ] |
333 ] |
314 return bool(pyqtPackages) |
334 return bool(pyqtPackages) |
315 |
335 |
316 def __checkUpgradeEric(self, packages): |
336 def __checkUpgradeEric(self, packages): |
317 """ |
337 """ |
318 Private method to check, if an upgrade of the eric-ide package is |
338 Private method to check, if an upgrade of the eric-ide package is |
319 attempted. |
339 attempted. |
320 |
340 |
321 @param packages list of packages to upgrade |
341 @param packages list of packages to upgrade |
322 @type list of str |
342 @type list of str |
323 @return flag indicating an eric-ide upgrade |
343 @return flag indicating an eric-ide upgrade |
324 @rtype bool |
344 @rtype bool |
325 """ |
345 """ |
326 ericPackages = [ |
346 ericPackages = [p for p in packages if p.lower() == "eric-ide"] |
327 p for p in packages if p.lower() == "eric-ide" |
|
328 ] |
|
329 return bool(ericPackages) |
347 return bool(ericPackages) |
330 |
348 |
331 def upgradePackages(self, packages, venvName, userSite=False): |
349 def upgradePackages(self, packages, venvName, userSite=False): |
332 """ |
350 """ |
333 Public method to upgrade the given list of packages. |
351 Public method to upgrade the given list of packages. |
334 |
352 |
335 @param packages list of packages to upgrade |
353 @param packages list of packages to upgrade |
336 @type list of str |
354 @type list of str |
337 @param venvName name of the virtual environment to be used |
355 @param venvName name of the virtual environment to be used |
338 @type str |
356 @type str |
339 @param userSite flag indicating an install to the user install |
357 @param userSite flag indicating an install to the user install |
413 if userSite: |
437 if userSite: |
414 args.append("--user") |
438 args.append("--user") |
415 if forceReinstall: |
439 if forceReinstall: |
416 args.append("--force-reinstall") |
440 args.append("--force-reinstall") |
417 args += packages |
441 args += packages |
418 dia = PipDialog(self.tr('Install Packages')) |
442 dia = PipDialog(self.tr("Install Packages")) |
419 res = dia.startProcess(interpreter, args) |
443 res = dia.startProcess(interpreter, args) |
420 if res: |
444 if res: |
421 dia.exec() |
445 dia.exec() |
422 |
446 |
423 def installRequirements(self, venvName): |
447 def installRequirements(self, venvName): |
424 """ |
448 """ |
425 Public method to install packages as given in a requirements file. |
449 Public method to install packages as given in a requirements file. |
426 |
450 |
427 @param venvName name of the virtual environment to be used |
451 @param venvName name of the virtual environment to be used |
428 @type str |
452 @type str |
429 """ |
453 """ |
430 from .PipFileSelectionDialog import PipFileSelectionDialog |
454 from .PipFileSelectionDialog import PipFileSelectionDialog |
|
455 |
431 dlg = PipFileSelectionDialog(self, "requirements") |
456 dlg = PipFileSelectionDialog(self, "requirements") |
432 if dlg.exec() == QDialog.DialogCode.Accepted: |
457 if dlg.exec() == QDialog.DialogCode.Accepted: |
433 requirements, user = dlg.getData() |
458 requirements, user = dlg.getData() |
434 if requirements and os.path.exists(requirements): |
459 if requirements and os.path.exists(requirements): |
435 interpreter = self.getVirtualenvInterpreter(venvName) |
460 interpreter = self.getVirtualenvInterpreter(venvName) |
436 if not interpreter: |
461 if not interpreter: |
437 return |
462 return |
438 |
463 |
439 if Preferences.getPip("PipSearchIndex"): |
464 if Preferences.getPip("PipSearchIndex"): |
440 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
465 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" |
441 args = ["-m", "pip", "install", "--index-url", indexUrl] |
466 args = ["-m", "pip", "install", "--index-url", indexUrl] |
442 else: |
467 else: |
443 args = ["-m", "pip", "install"] |
468 args = ["-m", "pip", "install"] |
444 if user: |
469 if user: |
445 args.append("--user") |
470 args.append("--user") |
446 args += ["--requirement", requirements] |
471 args += ["--requirement", requirements] |
447 dia = PipDialog(self.tr('Install Packages from Requirements')) |
472 dia = PipDialog(self.tr("Install Packages from Requirements")) |
448 res = dia.startProcess(interpreter, args) |
473 res = dia.startProcess(interpreter, args) |
449 if res: |
474 if res: |
450 dia.exec() |
475 dia.exec() |
451 |
476 |
452 def uninstallPackages(self, packages, venvName): |
477 def uninstallPackages(self, packages, venvName): |
453 """ |
478 """ |
454 Public method to uninstall the given list of packages. |
479 Public method to uninstall the given list of packages. |
455 |
480 |
456 @param packages list of packages to uninstall |
481 @param packages list of packages to uninstall |
457 @type list of str |
482 @type list of str |
458 @param venvName name of the virtual environment to be used |
483 @param venvName name of the virtual environment to be used |
459 @type str |
484 @type str |
460 @return flag indicating a successful execution |
485 @return flag indicating a successful execution |
461 @rtype bool |
486 @rtype bool |
462 """ |
487 """ |
463 res = False |
488 res = False |
464 if packages and venvName: |
489 if packages and venvName: |
465 from UI.DeleteFilesConfirmationDialog import ( |
490 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog |
466 DeleteFilesConfirmationDialog |
491 |
467 ) |
|
468 dlg = DeleteFilesConfirmationDialog( |
492 dlg = DeleteFilesConfirmationDialog( |
469 self.parent(), |
493 self.parent(), |
470 self.tr("Uninstall Packages"), |
494 self.tr("Uninstall Packages"), |
471 self.tr( |
495 self.tr("Do you really want to uninstall these packages?"), |
472 "Do you really want to uninstall these packages?"), |
496 packages, |
473 packages) |
497 ) |
474 if dlg.exec() == QDialog.DialogCode.Accepted: |
498 if dlg.exec() == QDialog.DialogCode.Accepted: |
475 interpreter = self.getVirtualenvInterpreter(venvName) |
499 interpreter = self.getVirtualenvInterpreter(venvName) |
476 if not interpreter: |
500 if not interpreter: |
477 return False |
501 return False |
478 args = ["-m", "pip", "uninstall", "--yes"] + packages |
502 args = ["-m", "pip", "uninstall", "--yes"] + packages |
479 dia = PipDialog(self.tr('Uninstall Packages')) |
503 dia = PipDialog(self.tr("Uninstall Packages")) |
480 res = dia.startProcess(interpreter, args) |
504 res = dia.startProcess(interpreter, args) |
481 if res: |
505 if res: |
482 dia.exec() |
506 dia.exec() |
483 return res |
507 return res |
484 |
508 |
485 def uninstallRequirements(self, venvName): |
509 def uninstallRequirements(self, venvName): |
486 """ |
510 """ |
487 Public method to uninstall packages as given in a requirements file. |
511 Public method to uninstall packages as given in a requirements file. |
488 |
512 |
489 @param venvName name of the virtual environment to be used |
513 @param venvName name of the virtual environment to be used |
490 @type str |
514 @type str |
491 """ |
515 """ |
492 if venvName: |
516 if venvName: |
493 from .PipFileSelectionDialog import PipFileSelectionDialog |
517 from .PipFileSelectionDialog import PipFileSelectionDialog |
494 dlg = PipFileSelectionDialog(self, "requirements", |
518 |
495 install=False) |
519 dlg = PipFileSelectionDialog(self, "requirements", install=False) |
496 if dlg.exec() == QDialog.DialogCode.Accepted: |
520 if dlg.exec() == QDialog.DialogCode.Accepted: |
497 requirements, _user = dlg.getData() |
521 requirements, _user = dlg.getData() |
498 if requirements and os.path.exists(requirements): |
522 if requirements and os.path.exists(requirements): |
499 try: |
523 try: |
500 with open(requirements, "r") as f: |
524 with open(requirements, "r") as f: |
501 reqs = f.read().splitlines() |
525 reqs = f.read().splitlines() |
502 except OSError: |
526 except OSError: |
503 return |
527 return |
504 |
528 |
505 from UI.DeleteFilesConfirmationDialog import ( |
529 from UI.DeleteFilesConfirmationDialog import ( |
506 DeleteFilesConfirmationDialog |
530 DeleteFilesConfirmationDialog, |
507 ) |
531 ) |
|
532 |
508 dlg = DeleteFilesConfirmationDialog( |
533 dlg = DeleteFilesConfirmationDialog( |
509 self.parent(), |
534 self.parent(), |
510 self.tr("Uninstall Packages"), |
535 self.tr("Uninstall Packages"), |
511 self.tr( |
536 self.tr("Do you really want to uninstall these packages?"), |
512 "Do you really want to uninstall these packages?"), |
537 reqs, |
513 reqs) |
538 ) |
514 if dlg.exec() == QDialog.DialogCode.Accepted: |
539 if dlg.exec() == QDialog.DialogCode.Accepted: |
515 interpreter = self.getVirtualenvInterpreter(venvName) |
540 interpreter = self.getVirtualenvInterpreter(venvName) |
516 if not interpreter: |
541 if not interpreter: |
517 return |
542 return |
518 |
543 |
519 args = ["-m", "pip", "uninstall", "--requirement", |
544 args = ["-m", "pip", "uninstall", "--requirement", requirements] |
520 requirements] |
545 dia = PipDialog(self.tr("Uninstall Packages from Requirements")) |
521 dia = PipDialog( |
|
522 self.tr('Uninstall Packages from Requirements')) |
|
523 res = dia.startProcess(interpreter, args) |
546 res = dia.startProcess(interpreter, args) |
524 if res: |
547 if res: |
525 dia.exec() |
548 dia.exec() |
526 |
549 |
527 def getIndexUrl(self): |
550 def getIndexUrl(self): |
528 """ |
551 """ |
529 Public method to get the index URL for PyPI. |
552 Public method to get the index URL for PyPI. |
530 |
553 |
531 @return index URL for PyPI |
554 @return index URL for PyPI |
532 @rtype str |
555 @rtype str |
533 """ |
556 """ |
534 indexUrl = ( |
557 indexUrl = ( |
535 Preferences.getPip("PipSearchIndex") + "/simple" |
558 Preferences.getPip("PipSearchIndex") + "/simple" |
536 if Preferences.getPip("PipSearchIndex") else |
559 if Preferences.getPip("PipSearchIndex") |
537 Pip.DefaultIndexUrlSimple |
560 else Pip.DefaultIndexUrlSimple |
538 ) |
561 ) |
539 |
562 |
540 return indexUrl |
563 return indexUrl |
541 |
564 |
542 def getIndexUrlPypi(self): |
565 def getIndexUrlPypi(self): |
543 """ |
566 """ |
544 Public method to get the index URL for PyPI API calls. |
567 Public method to get the index URL for PyPI API calls. |
545 |
568 |
546 @return index URL for XML RPC calls |
569 @return index URL for XML RPC calls |
547 @rtype str |
570 @rtype str |
548 """ |
571 """ |
549 indexUrl = ( |
572 indexUrl = ( |
550 Preferences.getPip("PipSearchIndex") + "/pypi" |
573 Preferences.getPip("PipSearchIndex") + "/pypi" |
551 if Preferences.getPip("PipSearchIndex") else |
574 if Preferences.getPip("PipSearchIndex") |
552 Pip.DefaultIndexUrlPypi |
575 else Pip.DefaultIndexUrlPypi |
553 ) |
576 ) |
554 |
577 |
555 return indexUrl |
578 return indexUrl |
556 |
579 |
557 def getIndexUrlSearch(self): |
580 def getIndexUrlSearch(self): |
558 """ |
581 """ |
559 Public method to get the index URL for PyPI API calls. |
582 Public method to get the index URL for PyPI API calls. |
560 |
583 |
561 @return index URL for XML RPC calls |
584 @return index URL for XML RPC calls |
562 @rtype str |
585 @rtype str |
563 """ |
586 """ |
564 indexUrl = ( |
587 indexUrl = ( |
565 Preferences.getPip("PipSearchIndex") + "/search/" |
588 Preferences.getPip("PipSearchIndex") + "/search/" |
566 if Preferences.getPip("PipSearchIndex") else |
589 if Preferences.getPip("PipSearchIndex") |
567 Pip.DefaultIndexUrlSearch |
590 else Pip.DefaultIndexUrlSearch |
568 ) |
591 ) |
569 |
592 |
570 return indexUrl |
593 return indexUrl |
571 |
594 |
572 def getInstalledPackages(self, envName, localPackages=True, |
595 def getInstalledPackages( |
573 notRequired=False, usersite=False): |
596 self, envName, localPackages=True, notRequired=False, usersite=False |
|
597 ): |
574 """ |
598 """ |
575 Public method to get the list of installed packages. |
599 Public method to get the list of installed packages. |
576 |
600 |
577 @param envName name of the environment to get the packages for |
601 @param envName name of the environment to get the packages for |
578 @type str |
602 @type str |
579 @param localPackages flag indicating to get local packages only |
603 @param localPackages flag indicating to get local packages only |
580 @type bool |
604 @type bool |
581 @param notRequired flag indicating to list packages that are not |
605 @param notRequired flag indicating to list packages that are not |
703 list of tuples containing the package name, installed version |
738 list of tuples containing the package name, installed version |
704 and available version |
739 and available version |
705 @rtype tuple of (bool, (str, str, str)) |
740 @rtype tuple of (bool, (str, str, str)) |
706 """ |
741 """ |
707 filteredPackages = [] |
742 filteredPackages = [] |
708 |
743 |
709 if bool(envName) and bool(packageStart): |
744 if bool(envName) and bool(packageStart): |
710 packages = self.getOutdatedPackages(envName) |
745 packages = self.getOutdatedPackages(envName) |
711 filterStr = packageStart.lower() |
746 filterStr = packageStart.lower() |
712 filteredPackages = [ |
747 filteredPackages = [ |
713 p for p in packages |
748 p for p in packages if p[0].lower().startswith(filterStr) |
714 if p[0].lower().startswith(filterStr)] |
749 ] |
715 |
750 |
716 return bool(filteredPackages), filteredPackages |
751 return bool(filteredPackages), filteredPackages |
717 |
752 |
718 def getPackageDetails(self, name, version): |
753 def getPackageDetails(self, name, version): |
719 """ |
754 """ |
720 Public method to get package details using the PyPI JSON interface. |
755 Public method to get package details using the PyPI JSON interface. |
721 |
756 |
722 @param name package name |
757 @param name package name |
723 @type str |
758 @type str |
724 @param version package version |
759 @param version package version |
725 @type str |
760 @type str |
726 @return dictionary containing PyPI package data |
761 @return dictionary containing PyPI package data |
727 @rtype dict |
762 @rtype dict |
728 """ |
763 """ |
729 result = {} |
764 result = {} |
730 |
765 |
731 if name and version: |
766 if name and version: |
732 url = "{0}/{1}/{2}/json".format( |
767 url = "{0}/{1}/{2}/json".format(self.getIndexUrlPypi(), name, version) |
733 self.getIndexUrlPypi(), name, version) |
|
734 request = QNetworkRequest(QUrl(url)) |
768 request = QNetworkRequest(QUrl(url)) |
735 reply = self.__networkManager.get(request) |
769 reply = self.__networkManager.get(request) |
736 while not reply.isFinished(): |
770 while not reply.isFinished(): |
737 QCoreApplication.processEvents() |
771 QCoreApplication.processEvents() |
738 QThread.msleep(100) |
772 QThread.msleep(100) |
739 |
773 |
740 reply.deleteLater() |
774 reply.deleteLater() |
741 if reply.error() == QNetworkReply.NetworkError.NoError: |
775 if reply.error() == QNetworkReply.NetworkError.NoError: |
742 data = str(reply.readAll(), |
776 data = str( |
743 Preferences.getSystem("IOEncoding"), |
777 reply.readAll(), Preferences.getSystem("IOEncoding"), "replace" |
744 'replace') |
778 ) |
745 with contextlib.suppress(Exception): |
779 with contextlib.suppress(Exception): |
746 result = json.loads(data) |
780 result = json.loads(data) |
747 |
781 |
748 return result |
782 return result |
749 |
783 |
750 def getPackageVersions(self, name): |
784 def getPackageVersions(self, name): |
751 """ |
785 """ |
752 Public method to get a list of versions available for the given |
786 Public method to get a list of versions available for the given |
753 package. |
787 package. |
754 |
788 |
755 @param name package name |
789 @param name package name |
756 @type str |
790 @type str |
757 @return list of available versions |
791 @return list of available versions |
758 @rtype list of str |
792 @rtype list of str |
759 """ |
793 """ |
760 result = [] |
794 result = [] |
761 |
795 |
762 if name: |
796 if name: |
763 url = "{0}/{1}/json".format(self.getIndexUrlPypi(), name) |
797 url = "{0}/{1}/json".format(self.getIndexUrlPypi(), name) |
764 request = QNetworkRequest(QUrl(url)) |
798 request = QNetworkRequest(QUrl(url)) |
765 reply = self.__networkManager.get(request) |
799 reply = self.__networkManager.get(request) |
766 while not reply.isFinished(): |
800 while not reply.isFinished(): |
767 QCoreApplication.processEvents() |
801 QCoreApplication.processEvents() |
768 QThread.msleep(100) |
802 QThread.msleep(100) |
769 |
803 |
770 reply.deleteLater() |
804 reply.deleteLater() |
771 if reply.error() == QNetworkReply.NetworkError.NoError: |
805 if reply.error() == QNetworkReply.NetworkError.NoError: |
772 dataStr = str(reply.readAll(), |
806 dataStr = str( |
773 Preferences.getSystem("IOEncoding"), |
807 reply.readAll(), Preferences.getSystem("IOEncoding"), "replace" |
774 'replace') |
808 ) |
775 with contextlib.suppress(Exception): |
809 with contextlib.suppress(Exception): |
776 data = json.loads(dataStr) |
810 data = json.loads(dataStr) |
777 result = list(data["releases"].keys()) |
811 result = list(data["releases"].keys()) |
778 |
812 |
779 return result |
813 return result |
780 |
814 |
781 def getFrozenPackages(self, envName, localPackages=True, usersite=False, |
815 def getFrozenPackages( |
782 requirement=None): |
816 self, envName, localPackages=True, usersite=False, requirement=None |
|
817 ): |
783 """ |
818 """ |
784 Public method to get the list of package specifiers to freeze them. |
819 Public method to get the list of package specifiers to freeze them. |
785 |
820 |
786 @param envName name of the environment to get the package specifiers |
821 @param envName name of the environment to get the package specifiers |
787 for |
822 for |
788 @type str |
823 @type str |
789 @param localPackages flag indicating to get package specifiers for |
824 @param localPackages flag indicating to get package specifiers for |
790 local packages only |
825 local packages only |
838 args = ["-m", "pip", "cache", "info"] |
875 args = ["-m", "pip", "cache", "info"] |
839 dia = PipDialog(self.tr("Cache Info")) |
876 dia = PipDialog(self.tr("Cache Info")) |
840 res = dia.startProcess(interpreter, args, showArgs=False) |
877 res = dia.startProcess(interpreter, args, showArgs=False) |
841 if res: |
878 if res: |
842 dia.exec() |
879 dia.exec() |
843 |
880 |
844 def cacheList(self, venvName): |
881 def cacheList(self, venvName): |
845 """ |
882 """ |
846 Public method to list files contained in the pip cache. |
883 Public method to list files contained in the pip cache. |
847 |
884 |
848 @param venvName name of the virtual environment to be used |
885 @param venvName name of the virtual environment to be used |
849 @type str |
886 @type str |
850 """ |
887 """ |
851 if venvName: |
888 if venvName: |
852 interpreter = self.getVirtualenvInterpreter(venvName) |
889 interpreter = self.getVirtualenvInterpreter(venvName) |
853 if interpreter: |
890 if interpreter: |
854 pattern, ok = QInputDialog.getText( |
891 pattern, ok = QInputDialog.getText( |
855 None, |
892 None, |
856 self.tr("List Cached Files"), |
893 self.tr("List Cached Files"), |
857 self.tr("Enter a file pattern (empty for all):"), |
894 self.tr("Enter a file pattern (empty for all):"), |
858 QLineEdit.EchoMode.Normal) |
895 QLineEdit.EchoMode.Normal, |
859 |
896 ) |
|
897 |
860 if ok: |
898 if ok: |
861 args = ["-m", "pip", "cache", "list"] |
899 args = ["-m", "pip", "cache", "list"] |
862 if pattern.strip(): |
900 if pattern.strip(): |
863 args.append(pattern.strip()) |
901 args.append(pattern.strip()) |
864 dia = PipDialog(self.tr("List Cached Files")) |
902 dia = PipDialog(self.tr("List Cached Files")) |
865 res = dia.startProcess(interpreter, args, |
903 res = dia.startProcess(interpreter, args, showArgs=False) |
866 showArgs=False) |
|
867 if res: |
904 if res: |
868 dia.exec() |
905 dia.exec() |
869 |
906 |
870 def cacheRemove(self, venvName): |
907 def cacheRemove(self, venvName): |
871 """ |
908 """ |
872 Public method to remove files from the pip cache. |
909 Public method to remove files from the pip cache. |
873 |
910 |
874 @param venvName name of the virtual environment to be used |
911 @param venvName name of the virtual environment to be used |
875 @type str |
912 @type str |
876 """ |
913 """ |
877 if venvName: |
914 if venvName: |
878 interpreter = self.getVirtualenvInterpreter(venvName) |
915 interpreter = self.getVirtualenvInterpreter(venvName) |
879 if interpreter: |
916 if interpreter: |
880 pattern, ok = QInputDialog.getText( |
917 pattern, ok = QInputDialog.getText( |
881 None, |
918 None, |
882 self.tr("Remove Cached Files"), |
919 self.tr("Remove Cached Files"), |
883 self.tr("Enter a file pattern:"), |
920 self.tr("Enter a file pattern:"), |
884 QLineEdit.EchoMode.Normal) |
921 QLineEdit.EchoMode.Normal, |
885 |
922 ) |
|
923 |
886 if ok and pattern.strip(): |
924 if ok and pattern.strip(): |
887 args = ["-m", "pip", "cache", "remove", pattern.strip()] |
925 args = ["-m", "pip", "cache", "remove", pattern.strip()] |
888 dia = PipDialog(self.tr("Remove Cached Files")) |
926 dia = PipDialog(self.tr("Remove Cached Files")) |
889 res = dia.startProcess(interpreter, args, |
927 res = dia.startProcess(interpreter, args, showArgs=False) |
890 showArgs=False) |
|
891 if res: |
928 if res: |
892 dia.exec() |
929 dia.exec() |
893 |
930 |
894 def cachePurge(self, venvName): |
931 def cachePurge(self, venvName): |
895 """ |
932 """ |
896 Public method to remove all files from the pip cache. |
933 Public method to remove all files from the pip cache. |
897 |
934 |
898 @param venvName name of the virtual environment to be used |
935 @param venvName name of the virtual environment to be used |
899 @type str |
936 @type str |
900 """ |
937 """ |
901 if venvName: |
938 if venvName: |
902 interpreter = self.getVirtualenvInterpreter(venvName) |
939 interpreter = self.getVirtualenvInterpreter(venvName) |
903 if interpreter: |
940 if interpreter: |
904 ok = EricMessageBox.yesNo( |
941 ok = EricMessageBox.yesNo( |
905 None, |
942 None, |
906 self.tr("Purge Cache"), |
943 self.tr("Purge Cache"), |
907 self.tr("Do you really want to purge the pip cache? All" |
944 self.tr( |
908 " files need to be downloaded again.")) |
945 "Do you really want to purge the pip cache? All" |
|
946 " files need to be downloaded again." |
|
947 ), |
|
948 ) |
909 if ok: |
949 if ok: |
910 args = ["-m", "pip", "cache", "purge"] |
950 args = ["-m", "pip", "cache", "purge"] |
911 dia = PipDialog(self.tr("Purge Cache")) |
951 dia = PipDialog(self.tr("Purge Cache")) |
912 res = dia.startProcess(interpreter, args, |
952 res = dia.startProcess(interpreter, args, showArgs=False) |
913 showArgs=False) |
|
914 if res: |
953 if res: |
915 dia.exec() |
954 dia.exec() |
916 |
955 |
917 ####################################################################### |
956 ####################################################################### |
918 ## Dependency tree handling methods below |
957 ## Dependency tree handling methods below |
919 ####################################################################### |
958 ####################################################################### |
920 |
959 |
921 def getDependencyTree(self, envName, localPackages=True, usersite=False, |
960 def getDependencyTree( |
922 reverse=False): |
961 self, envName, localPackages=True, usersite=False, reverse=False |
|
962 ): |
923 """ |
963 """ |
924 Public method to get the dependency tree of installed packages. |
964 Public method to get the dependency tree of installed packages. |
925 |
965 |
926 @param envName name of the environment to get the packages for |
966 @param envName name of the environment to get the packages for |
927 @type str |
967 @type str |
928 @param localPackages flag indicating to get the tree for local |
968 @param localPackages flag indicating to get the tree for local |
929 packages only |
969 packages only |
930 @type bool |
970 @type bool |