src/eric7/PipInterface/Pip.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9218
71cf3979a6c9
child 9260
eb19dcb8d852
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
10 import os 10 import os
11 import sys 11 import sys
12 import json 12 import json
13 import contextlib 13 import contextlib
14 14
15 from PyQt6.QtCore import ( 15 from PyQt6.QtCore import pyqtSlot, QObject, QProcess, QUrl, QCoreApplication, QThread
16 pyqtSlot, QObject, QProcess, QUrl, QCoreApplication, QThread
17 )
18 from PyQt6.QtWidgets import QDialog, QInputDialog, QLineEdit 16 from PyQt6.QtWidgets import QDialog, QInputDialog, QLineEdit
19 from PyQt6.QtNetwork import ( 17 from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
20 QNetworkAccessManager, QNetworkRequest, QNetworkReply
21 )
22 18
23 from EricWidgets import EricMessageBox 19 from EricWidgets import EricMessageBox
24 from EricWidgets.EricApplication import ericApp 20 from EricWidgets.EricApplication import ericApp
25 21
26 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired 22 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired
23
27 try: 24 try:
28 from EricNetwork.EricSslErrorHandler import EricSslErrorHandler 25 from EricNetwork.EricSslErrorHandler import EricSslErrorHandler
26
29 SSL_AVAILABLE = True 27 SSL_AVAILABLE = True
30 except ImportError: 28 except ImportError:
31 SSL_AVAILABLE = False 29 SSL_AVAILABLE = False
32 30
33 from .PipDialog import PipDialog 31 from .PipDialog import PipDialog
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
342 @return flag indicating a successful execution 360 @return flag indicating a successful execution
343 @rtype bool 361 @rtype bool
344 """ 362 """
345 if not venvName: 363 if not venvName:
346 return False 364 return False
347 365
348 if self.getVirtualenvInterpreter(venvName) in ( 366 if self.getVirtualenvInterpreter(venvName) in (
349 sys.executable, Globals.getPythonExecutable() 367 sys.executable,
368 Globals.getPythonExecutable(),
350 ): 369 ):
351 upgradePyQt = self.__checkUpgradePyQt(packages) 370 upgradePyQt = self.__checkUpgradePyQt(packages)
352 upgradeEric = self.__checkUpgradeEric(packages) 371 upgradeEric = self.__checkUpgradeEric(packages)
353 if upgradeEric or upgradePyQt: 372 if upgradeEric or upgradePyQt:
354 try: 373 try:
356 self.__ui.upgradeEricPyQt() 375 self.__ui.upgradeEricPyQt()
357 elif upgradeEric: 376 elif upgradeEric:
358 self.__ui.upgradeEric() 377 self.__ui.upgradeEric()
359 elif upgradePyQt: 378 elif upgradePyQt:
360 self.__ui.upgradePyQt() 379 self.__ui.upgradePyQt()
361 return None # should not be reached; play it safe 380 return None # should not be reached; play it safe
362 except AttributeError: 381 except AttributeError:
363 return False 382 return False
364 383
365 interpreter = self.getVirtualenvInterpreter(venvName) 384 interpreter = self.getVirtualenvInterpreter(venvName)
366 if not interpreter: 385 if not interpreter:
367 return False 386 return False
368 387
369 if Preferences.getPip("PipSearchIndex"): 388 if Preferences.getPip("PipSearchIndex"):
370 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" 389 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple"
371 args = ["-m", "pip", "install", "--index-url", indexUrl, 390 args = ["-m", "pip", "install", "--index-url", indexUrl, "--upgrade"]
372 "--upgrade"]
373 else: 391 else:
374 args = ["-m", "pip", "install", "--upgrade"] 392 args = ["-m", "pip", "install", "--upgrade"]
375 if userSite: 393 if userSite:
376 args.append("--user") 394 args.append("--user")
377 args += packages 395 args += packages
378 dia = PipDialog(self.tr('Upgrade Packages')) 396 dia = PipDialog(self.tr("Upgrade Packages"))
379 res = dia.startProcess(interpreter, args) 397 res = dia.startProcess(interpreter, args)
380 if res: 398 if res:
381 dia.exec() 399 dia.exec()
382 return res 400 return res
383 401
384 def installPackages(self, packages, venvName="", userSite=False, 402 def installPackages(
385 interpreter="", forceReinstall=False): 403 self,
404 packages,
405 venvName="",
406 userSite=False,
407 interpreter="",
408 forceReinstall=False,
409 ):
386 """ 410 """
387 Public method to install the given list of packages. 411 Public method to install the given list of packages.
388 412
389 @param packages list of packages to install 413 @param packages list of packages to install
390 @type list of str 414 @type list of str
391 @param venvName name of the virtual environment to be used 415 @param venvName name of the virtual environment to be used
392 @type str 416 @type str
393 @param userSite flag indicating an install to the user install 417 @param userSite flag indicating an install to the user install
401 """ 425 """
402 if venvName: 426 if venvName:
403 interpreter = self.getVirtualenvInterpreter(venvName) 427 interpreter = self.getVirtualenvInterpreter(venvName)
404 if not interpreter: 428 if not interpreter:
405 return 429 return
406 430
407 if interpreter: 431 if interpreter:
408 if Preferences.getPip("PipSearchIndex"): 432 if Preferences.getPip("PipSearchIndex"):
409 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" 433 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple"
410 args = ["-m", "pip", "install", "--index-url", indexUrl] 434 args = ["-m", "pip", "install", "--index-url", indexUrl]
411 else: 435 else:
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
586 @type bool 610 @type bool
587 @return list of tuples containing the package name and version 611 @return list of tuples containing the package name and version
588 @rtype list of tuple of (str, str) 612 @rtype list of tuple of (str, str)
589 """ 613 """
590 packages = [] 614 packages = []
591 615
592 if envName: 616 if envName:
593 interpreter = self.getVirtualenvInterpreter(envName) 617 interpreter = self.getVirtualenvInterpreter(envName)
594 if interpreter: 618 if interpreter:
595 args = [ 619 args = [
596 "-m", "pip", 620 "-m",
621 "pip",
597 "list", 622 "list",
598 "--format=json", 623 "--format=json",
599 ] 624 ]
600 if localPackages: 625 if localPackages:
601 args.append("--local") 626 args.append("--local")
602 if notRequired: 627 if notRequired:
603 args.append("--not-required") 628 args.append("--not-required")
604 if usersite: 629 if usersite:
605 args.append("--user") 630 args.append("--user")
606 631
607 if Preferences.getPip("PipSearchIndex"): 632 if Preferences.getPip("PipSearchIndex"):
608 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" 633 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple"
609 args += ["--index-url", indexUrl] 634 args += ["--index-url", indexUrl]
610 635
611 proc = QProcess() 636 proc = QProcess()
612 proc.start(interpreter, args) 637 proc.start(interpreter, args)
613 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 638 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
614 output = str(proc.readAllStandardOutput(), 639 output = str(
615 Preferences.getSystem("IOEncoding"), 640 proc.readAllStandardOutput(),
616 'replace').strip() 641 Preferences.getSystem("IOEncoding"),
642 "replace",
643 ).strip()
617 try: 644 try:
618 jsonList = json.loads(output) 645 jsonList = json.loads(output)
619 except Exception: 646 except Exception:
620 jsonList = [] 647 jsonList = []
621 648
622 for package in jsonList: 649 for package in jsonList:
623 if isinstance(package, dict): 650 if isinstance(package, dict):
624 packages.append(( 651 packages.append(
625 package["name"], 652 (
626 package["version"], 653 package["name"],
627 )) 654 package["version"],
628 655 )
656 )
657
629 return packages 658 return packages
630 659
631 def getOutdatedPackages(self, envName, localPackages=True, 660 def getOutdatedPackages(
632 notRequired=False, usersite=False): 661 self, envName, localPackages=True, notRequired=False, usersite=False
662 ):
633 """ 663 """
634 Public method to get the list of outdated packages. 664 Public method to get the list of outdated packages.
635 665
636 @param envName name of the environment to get the packages for 666 @param envName name of the environment to get the packages for
637 @type str 667 @type str
638 @param localPackages flag indicating to get local packages only 668 @param localPackages flag indicating to get local packages only
639 @type bool 669 @type bool
640 @param notRequired flag indicating to list packages that are not 670 @param notRequired flag indicating to list packages that are not
646 @return list of tuples containing the package name, installed version 676 @return list of tuples containing the package name, installed version
647 and available version 677 and available version
648 @rtype list of tuple of (str, str, str) 678 @rtype list of tuple of (str, str, str)
649 """ 679 """
650 packages = [] 680 packages = []
651 681
652 if envName: 682 if envName:
653 interpreter = self.getVirtualenvInterpreter(envName) 683 interpreter = self.getVirtualenvInterpreter(envName)
654 if interpreter: 684 if interpreter:
655 args = [ 685 args = [
656 "-m", "pip", 686 "-m",
687 "pip",
657 "list", 688 "list",
658 "--outdated", 689 "--outdated",
659 "--format=json", 690 "--format=json",
660 ] 691 ]
661 if localPackages: 692 if localPackages:
662 args.append("--local") 693 args.append("--local")
663 if notRequired: 694 if notRequired:
664 args.append("--not-required") 695 args.append("--not-required")
665 if usersite: 696 if usersite:
666 args.append("--user") 697 args.append("--user")
667 698
668 if Preferences.getPip("PipSearchIndex"): 699 if Preferences.getPip("PipSearchIndex"):
669 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple" 700 indexUrl = Preferences.getPip("PipSearchIndex") + "/simple"
670 args += ["--index-url", indexUrl] 701 args += ["--index-url", indexUrl]
671 702
672 proc = QProcess() 703 proc = QProcess()
673 proc.start(interpreter, args) 704 proc.start(interpreter, args)
674 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 705 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
675 output = str(proc.readAllStandardOutput(), 706 output = str(
676 Preferences.getSystem("IOEncoding"), 707 proc.readAllStandardOutput(),
677 'replace').strip() 708 Preferences.getSystem("IOEncoding"),
709 "replace",
710 ).strip()
678 try: 711 try:
679 jsonList = json.loads(output) 712 jsonList = json.loads(output)
680 except Exception: 713 except Exception:
681 jsonList = [] 714 jsonList = []
682 715
683 for package in jsonList: 716 for package in jsonList:
684 if isinstance(package, dict): 717 if isinstance(package, dict):
685 packages.append(( 718 packages.append(
686 package["name"], 719 (
687 package["version"], 720 package["name"],
688 package["latest_version"], 721 package["version"],
689 )) 722 package["latest_version"],
690 723 )
724 )
725
691 return packages 726 return packages
692 727
693 def checkPackageOutdated(self, packageStart, envName): 728 def checkPackageOutdated(self, packageStart, envName):
694 """ 729 """
695 Public method to check, if a group of packages is outdated. 730 Public method to check, if a group of packages is outdated.
696 731
697 @param packageStart start string for package names to be checked 732 @param packageStart start string for package names to be checked
698 (case insensitive) 733 (case insensitive)
699 @type str 734 @type str
700 @param envName name of the environment to get the packages for 735 @param envName name of the environment to get the packages for
701 @type str 736 @type str
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
796 @type str 831 @type str
797 @return list of package specifiers 832 @return list of package specifiers
798 @rtype list of str 833 @rtype list of str
799 """ 834 """
800 specifiers = [] 835 specifiers = []
801 836
802 if envName: 837 if envName:
803 interpreter = self.getVirtualenvInterpreter(envName) 838 interpreter = self.getVirtualenvInterpreter(envName)
804 if interpreter: 839 if interpreter:
805 args = [ 840 args = [
806 "-m", "pip", 841 "-m",
842 "pip",
807 "freeze", 843 "freeze",
808 ] 844 ]
809 if localPackages: 845 if localPackages:
810 args.append("--local") 846 args.append("--local")
811 if usersite: 847 if usersite:
812 args.append("--user") 848 args.append("--user")
813 if requirement and os.path.exists(requirement): 849 if requirement and os.path.exists(requirement):
814 args.append("--requirement") 850 args.append("--requirement")
815 args.append(requirement) 851 args.append(requirement)
816 852
817 success, output = self.runProcess(args, interpreter) 853 success, output = self.runProcess(args, interpreter)
818 if success and output: 854 if success and output:
819 specifiers = [spec.strip() for spec in output.splitlines() 855 specifiers = [
820 if spec.strip()] 856 spec.strip() for spec in output.splitlines() if spec.strip()
821 857 ]
858
822 return specifiers 859 return specifiers
823 860
824 ####################################################################### 861 #######################################################################
825 ## Cache handling methods below 862 ## Cache handling methods below
826 ####################################################################### 863 #######################################################################
827 864
828 def showCacheInfo(self, venvName): 865 def showCacheInfo(self, venvName):
829 """ 866 """
830 Public method to show some information about the pip cache. 867 Public method to show some information about the pip cache.
831 868
832 @param venvName name of the virtual environment to be used 869 @param venvName name of the virtual environment to be used
833 @type str 870 @type str
834 """ 871 """
835 if venvName: 872 if venvName:
836 interpreter = self.getVirtualenvInterpreter(venvName) 873 interpreter = self.getVirtualenvInterpreter(venvName)
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
937 @return list of nested dictionaries resembling the requested 977 @return list of nested dictionaries resembling the requested
938 dependency tree 978 dependency tree
939 @rtype list of dict 979 @rtype list of dict
940 """ 980 """
941 dependencies = [] 981 dependencies = []
942 982
943 if envName: 983 if envName:
944 interpreter = self.getVirtualenvInterpreter(envName) 984 interpreter = self.getVirtualenvInterpreter(envName)
945 if interpreter: 985 if interpreter:
946 from . import pipdeptree 986 from . import pipdeptree
987
947 with open(pipdeptree.__file__, "r") as f: 988 with open(pipdeptree.__file__, "r") as f:
948 content = f.read() 989 content = f.read()
949 args = [ 990 args = [
950 "-c", 991 "-c",
951 content, 992 content,
955 args.append("--local-only") 996 args.append("--local-only")
956 if usersite: 997 if usersite:
957 args.append("--user-only") 998 args.append("--user-only")
958 if reverse: 999 if reverse:
959 args.append("--reverse") 1000 args.append("--reverse")
960 1001
961 proc = QProcess() 1002 proc = QProcess()
962 proc.start(interpreter, args) 1003 proc.start(interpreter, args)
963 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 1004 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
964 output = str(proc.readAllStandardOutput(), 1005 output = str(
965 Preferences.getSystem("IOEncoding"), 1006 proc.readAllStandardOutput(),
966 'replace').strip() 1007 Preferences.getSystem("IOEncoding"),
1008 "replace",
1009 ).strip()
967 with contextlib.suppress(json.JSONDecodeError): 1010 with contextlib.suppress(json.JSONDecodeError):
968 dependencies = json.loads(output) 1011 dependencies = json.loads(output)
969 1012
970 return dependencies 1013 return dependencies
971 1014
972 ####################################################################### 1015 #######################################################################
973 ## License handling methods below 1016 ## License handling methods below
974 ####################################################################### 1017 #######################################################################
975 1018
976 def getLicenses(self, envName, localPackages=True, usersite=False, 1019 def getLicenses(self, envName, localPackages=True, usersite=False, summary=False):
977 summary=False):
978 """ 1020 """
979 Public method to get the licenses per package for a given environment. 1021 Public method to get the licenses per package for a given environment.
980 1022
981 @param envName name of the environment to get the licenses for 1023 @param envName name of the environment to get the licenses for
982 @type str 1024 @type str
983 @param localPackages flag indicating to get the licenses for local 1025 @param localPackages flag indicating to get the licenses for local
984 packages only 1026 packages only
985 @type bool 1027 @type bool
992 @return list of dictionaries containing the license and version per 1034 @return list of dictionaries containing the license and version per
993 package 1035 package
994 @rtype dict 1036 @rtype dict
995 """ 1037 """
996 licenses = [] 1038 licenses = []
997 1039
998 if envName: 1040 if envName:
999 interpreter = self.getVirtualenvInterpreter(envName) 1041 interpreter = self.getVirtualenvInterpreter(envName)
1000 if interpreter: 1042 if interpreter:
1001 from . import piplicenses 1043 from . import piplicenses
1044
1002 with open(piplicenses.__file__, "r") as f: 1045 with open(piplicenses.__file__, "r") as f:
1003 content = f.read() 1046 content = f.read()
1004 args = [ 1047 args = [
1005 "-c", 1048 "-c",
1006 content, 1049 content,
1015 args.append("--local-only") 1058 args.append("--local-only")
1016 if usersite: 1059 if usersite:
1017 args.append("--user-only") 1060 args.append("--user-only")
1018 if summary: 1061 if summary:
1019 args.append("--summary") 1062 args.append("--summary")
1020 1063
1021 proc = QProcess() 1064 proc = QProcess()
1022 proc.start(interpreter, args) 1065 proc.start(interpreter, args)
1023 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 1066 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
1024 output = str(proc.readAllStandardOutput(), 1067 output = str(
1025 Preferences.getSystem("IOEncoding"), 1068 proc.readAllStandardOutput(),
1026 'replace').strip() 1069 Preferences.getSystem("IOEncoding"),
1070 "replace",
1071 ).strip()
1027 with contextlib.suppress(json.JSONDecodeError): 1072 with contextlib.suppress(json.JSONDecodeError):
1028 licenses = json.loads(output) 1073 licenses = json.loads(output)
1029 1074
1030 return licenses 1075 return licenses
1031 1076
1032 def getLicensesSummary(self, envName, localPackages=True, usersite=False): 1077 def getLicensesSummary(self, envName, localPackages=True, usersite=False):
1033 """ 1078 """
1034 Public method to get a summary of licenses found in a given 1079 Public method to get a summary of licenses found in a given
1035 environment. 1080 environment.
1036 1081
1037 @param envName name of the environment to get the licenses summary for 1082 @param envName name of the environment to get the licenses summary for
1038 @type str 1083 @type str
1039 @param localPackages flag indicating to get the licenses summary for 1084 @param localPackages flag indicating to get the licenses summary for
1040 local packages only 1085 local packages only
1041 @type bool 1086 @type bool
1044 @type bool 1089 @type bool
1045 @return list of dictionaries containing the license and the count of 1090 @return list of dictionaries containing the license and the count of
1046 packages 1091 packages
1047 @rtype dict 1092 @rtype dict
1048 """ 1093 """
1049 return self.getLicenses(envName, localPackages=localPackages, 1094 return self.getLicenses(
1050 usersite=usersite, summary=True) 1095 envName, localPackages=localPackages, usersite=usersite, summary=True
1096 )

eric ide

mercurial