src/eric7/CondaInterface/Conda.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9278
36448ca469c2
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
24 24
25 25
26 class Conda(QObject): 26 class Conda(QObject):
27 """ 27 """
28 Class implementing the conda GUI logic. 28 Class implementing the conda GUI logic.
29 29
30 @signal condaEnvironmentCreated() emitted to indicate the creation of 30 @signal condaEnvironmentCreated() emitted to indicate the creation of
31 a new environment 31 a new environment
32 @signal condaEnvironmentRemoved() emitted to indicate the removal of 32 @signal condaEnvironmentRemoved() emitted to indicate the removal of
33 an environment 33 an environment
34 """ 34 """
35
35 condaEnvironmentCreated = pyqtSignal() 36 condaEnvironmentCreated = pyqtSignal()
36 condaEnvironmentRemoved = pyqtSignal() 37 condaEnvironmentRemoved = pyqtSignal()
37 38
38 RootName = QCoreApplication.translate("Conda", "<root>") 39 RootName = QCoreApplication.translate("Conda", "<root>")
39 40
40 def __init__(self, parent=None): 41 def __init__(self, parent=None):
41 """ 42 """
42 Constructor 43 Constructor
43 44
44 @param parent parent 45 @param parent parent
45 @type QObject 46 @type QObject
46 """ 47 """
47 super().__init__(parent) 48 super().__init__(parent)
48 49
49 self.__ui = parent 50 self.__ui = parent
50 51
51 ####################################################################### 52 #######################################################################
52 ## environment related methods below 53 ## environment related methods below
53 ####################################################################### 54 #######################################################################
54 55
55 def createCondaEnvironment(self, arguments): 56 def createCondaEnvironment(self, arguments):
56 """ 57 """
57 Public method to create a conda environment. 58 Public method to create a conda environment.
58 59
59 @param arguments list of command line arguments 60 @param arguments list of command line arguments
60 @type list of str 61 @type list of str
61 @return tuple containing a flag indicating success, the directory of 62 @return tuple containing a flag indicating success, the directory of
62 the created environment (aka. prefix) and the corresponding Python 63 the created environment (aka. prefix) and the corresponding Python
63 interpreter 64 interpreter
64 @rtype tuple of (bool, str, str) 65 @rtype tuple of (bool, str, str)
65 """ 66 """
66 args = ["create", "--json", "--yes"] + arguments 67 args = ["create", "--json", "--yes"] + arguments
67 68
68 dlg = CondaExecDialog("create", self.__ui) 69 dlg = CondaExecDialog("create", self.__ui)
69 dlg.start(args) 70 dlg.start(args)
70 dlg.exec() 71 dlg.exec()
71 ok, resultDict = dlg.getResult() 72 ok, resultDict = dlg.getResult()
72 73
73 if ok: 74 if ok:
74 if ("actions" in resultDict and 75 if "actions" in resultDict and "PREFIX" in resultDict["actions"]:
75 "PREFIX" in resultDict["actions"]):
76 prefix = resultDict["actions"]["PREFIX"] 76 prefix = resultDict["actions"]["PREFIX"]
77 elif "prefix" in resultDict: 77 elif "prefix" in resultDict:
78 prefix = resultDict["prefix"] 78 prefix = resultDict["prefix"]
79 elif "dst_prefix" in resultDict: 79 elif "dst_prefix" in resultDict:
80 prefix = resultDict["dst_prefix"] 80 prefix = resultDict["dst_prefix"]
81 else: 81 else:
82 prefix = "" 82 prefix = ""
83 83
84 # determine Python executable 84 # determine Python executable
85 if prefix: 85 if prefix:
86 pathPrefixes = [ 86 pathPrefixes = [prefix, rootPrefix()]
87 prefix,
88 rootPrefix()
89 ]
90 else: 87 else:
91 pathPrefixes = [ 88 pathPrefixes = [rootPrefix()]
92 rootPrefix()
93 ]
94 for pathPrefix in pathPrefixes: 89 for pathPrefix in pathPrefixes:
95 python = ( 90 python = (
96 os.path.join(pathPrefix, "python.exe") 91 os.path.join(pathPrefix, "python.exe")
97 if Globals.isWindowsPlatform() else 92 if Globals.isWindowsPlatform()
98 os.path.join(pathPrefix, "bin", "python") 93 else os.path.join(pathPrefix, "bin", "python")
99 ) 94 )
100 if os.path.exists(python): 95 if os.path.exists(python):
101 break 96 break
102 else: 97 else:
103 python = "" 98 python = ""
104 99
105 self.condaEnvironmentCreated.emit() 100 self.condaEnvironmentCreated.emit()
106 return True, prefix, python 101 return True, prefix, python
107 else: 102 else:
108 return False, "", "" 103 return False, "", ""
109 104
110 def removeCondaEnvironment(self, name="", prefix=""): 105 def removeCondaEnvironment(self, name="", prefix=""):
111 """ 106 """
112 Public method to remove a conda environment. 107 Public method to remove a conda environment.
113 108
114 @param name name of the environment 109 @param name name of the environment
115 @type str 110 @type str
116 @param prefix prefix of the environment 111 @param prefix prefix of the environment
117 @type str 112 @type str
118 @return flag indicating success 113 @return flag indicating success
119 @rtype bool 114 @rtype bool
120 @exception RuntimeError raised to indicate an error in parameters 115 @exception RuntimeError raised to indicate an error in parameters
121 116
122 Note: only one of name or prefix must be given. 117 Note: only one of name or prefix must be given.
123 """ 118 """
124 if name and prefix: 119 if name and prefix:
125 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 120 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
126 121
127 if not name and not prefix: 122 if not name and not prefix:
128 raise RuntimeError("One of 'name' or 'prefix' must be given.") 123 raise RuntimeError("One of 'name' or 'prefix' must be given.")
129 124
130 args = [ 125 args = [
131 "remove", 126 "remove",
132 "--json", 127 "--json",
133 "--quiet", 128 "--quiet",
134 "--all", 129 "--all",
135 ] 130 ]
136 if name: 131 if name:
137 args.extend(["--name", name]) 132 args.extend(["--name", name])
138 elif prefix: 133 elif prefix:
139 args.extend(["--prefix", prefix]) 134 args.extend(["--prefix", prefix])
140 135
141 exe = Preferences.getConda("CondaExecutable") 136 exe = Preferences.getConda("CondaExecutable")
142 if not exe: 137 if not exe:
143 exe = "conda" 138 exe = "conda"
144 139
145 proc = QProcess() 140 proc = QProcess()
146 proc.start(exe, args) 141 proc.start(exe, args)
147 if not proc.waitForStarted(15000): 142 if not proc.waitForStarted(15000):
148 EricMessageBox.critical( 143 EricMessageBox.critical(
149 self.__ui, 144 self.__ui,
150 self.tr("conda remove"), 145 self.tr("conda remove"),
151 self.tr("""The conda executable could not be started.""")) 146 self.tr("""The conda executable could not be started."""),
147 )
152 return False 148 return False
153 else: 149 else:
154 proc.waitForFinished(15000) 150 proc.waitForFinished(15000)
155 output = str(proc.readAllStandardOutput(), 151 output = str(
156 Preferences.getSystem("IOEncoding"), 152 proc.readAllStandardOutput(),
157 'replace').strip() 153 Preferences.getSystem("IOEncoding"),
154 "replace",
155 ).strip()
158 try: 156 try:
159 jsonDict = json.loads(output) 157 jsonDict = json.loads(output)
160 except Exception: 158 except Exception:
161 EricMessageBox.critical( 159 EricMessageBox.critical(
162 self.__ui, 160 self.__ui,
163 self.tr("conda remove"), 161 self.tr("conda remove"),
164 self.tr("""The conda executable returned invalid data.""")) 162 self.tr("""The conda executable returned invalid data."""),
163 )
165 return False 164 return False
166 165
167 if "error" in jsonDict: 166 if "error" in jsonDict:
168 EricMessageBox.critical( 167 EricMessageBox.critical(
169 self.__ui, 168 self.__ui,
170 self.tr("conda remove"), 169 self.tr("conda remove"),
171 self.tr("<p>The conda executable returned an error.</p>" 170 self.tr(
172 "<p>{0}</p>").format(jsonDict["message"])) 171 "<p>The conda executable returned an error.</p>" "<p>{0}</p>"
172 ).format(jsonDict["message"]),
173 )
173 return False 174 return False
174 175
175 if jsonDict["success"]: 176 if jsonDict["success"]:
176 self.condaEnvironmentRemoved.emit() 177 self.condaEnvironmentRemoved.emit()
177 178
178 return jsonDict["success"] 179 return jsonDict["success"]
179 180
180 return False 181 return False
181 182
182 def getCondaEnvironmentsList(self): 183 def getCondaEnvironmentsList(self):
183 """ 184 """
184 Public method to get a list of all Conda environments. 185 Public method to get a list of all Conda environments.
185 186
186 @return list of tuples containing the environment name and prefix 187 @return list of tuples containing the environment name and prefix
187 @rtype list of tuples of (str, str) 188 @rtype list of tuples of (str, str)
188 """ 189 """
189 exe = Preferences.getConda("CondaExecutable") 190 exe = Preferences.getConda("CondaExecutable")
190 if not exe: 191 if not exe:
191 exe = "conda" 192 exe = "conda"
192 193
193 environmentsList = [] 194 environmentsList = []
194 195
195 proc = QProcess() 196 proc = QProcess()
196 proc.start(exe, ["info", "--json"]) 197 proc.start(exe, ["info", "--json"])
197 if proc.waitForStarted(15000) and proc.waitForFinished(15000): 198 if proc.waitForStarted(15000) and proc.waitForFinished(15000):
198 output = str(proc.readAllStandardOutput(), 199 output = str(
199 Preferences.getSystem("IOEncoding"), 200 proc.readAllStandardOutput(),
200 'replace').strip() 201 Preferences.getSystem("IOEncoding"),
202 "replace",
203 ).strip()
201 try: 204 try:
202 jsonDict = json.loads(output) 205 jsonDict = json.loads(output)
203 except Exception: 206 except Exception:
204 jsonDict = {} 207 jsonDict = {}
205 208
206 if "envs" in jsonDict: 209 if "envs" in jsonDict:
207 for prefix in jsonDict["envs"][:]: 210 for prefix in jsonDict["envs"][:]:
208 if prefix == jsonDict["root_prefix"]: 211 if prefix == jsonDict["root_prefix"]:
209 if not jsonDict["root_writable"]: 212 if not jsonDict["root_writable"]:
210 # root prefix is listed but not writable 213 # root prefix is listed but not writable
211 continue 214 continue
212 name = self.RootName 215 name = self.RootName
213 else: 216 else:
214 name = os.path.basename(prefix) 217 name = os.path.basename(prefix)
215 218
216 environmentsList.append((name, prefix)) 219 environmentsList.append((name, prefix))
217 220
218 return environmentsList 221 return environmentsList
219 222
220 ####################################################################### 223 #######################################################################
221 ## package related methods below 224 ## package related methods below
222 ####################################################################### 225 #######################################################################
223 226
224 def getInstalledPackages(self, name="", prefix=""): 227 def getInstalledPackages(self, name="", prefix=""):
225 """ 228 """
226 Public method to get a list of installed packages of a conda 229 Public method to get a list of installed packages of a conda
227 environment. 230 environment.
228 231
229 @param name name of the environment 232 @param name name of the environment
230 @type str 233 @type str
231 @param prefix prefix of the environment 234 @param prefix prefix of the environment
232 @type str 235 @type str
233 @return list of installed packages. Each entry is a tuple containing 236 @return list of installed packages. Each entry is a tuple containing
234 the package name, version and build. 237 the package name, version and build.
235 @rtype list of tuples of (str, str, str) 238 @rtype list of tuples of (str, str, str)
236 @exception RuntimeError raised to indicate an error in parameters 239 @exception RuntimeError raised to indicate an error in parameters
237 240
238 Note: only one of name or prefix must be given. 241 Note: only one of name or prefix must be given.
239 """ 242 """
240 if name and prefix: 243 if name and prefix:
241 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 244 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
242 245
243 if not name and not prefix: 246 if not name and not prefix:
244 raise RuntimeError("One of 'name' or 'prefix' must be given.") 247 raise RuntimeError("One of 'name' or 'prefix' must be given.")
245 248
246 args = [ 249 args = [
247 "list", 250 "list",
248 "--json", 251 "--json",
249 ] 252 ]
250 if name: 253 if name:
251 args.extend(["--name", name]) 254 args.extend(["--name", name])
252 elif prefix: 255 elif prefix:
253 args.extend(["--prefix", prefix]) 256 args.extend(["--prefix", prefix])
254 257
255 exe = Preferences.getConda("CondaExecutable") 258 exe = Preferences.getConda("CondaExecutable")
256 if not exe: 259 if not exe:
257 exe = "conda" 260 exe = "conda"
258 261
259 packages = [] 262 packages = []
260 263
261 proc = QProcess() 264 proc = QProcess()
262 proc.start(exe, args) 265 proc.start(exe, args)
263 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 266 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
264 output = str(proc.readAllStandardOutput(), 267 output = str(
265 Preferences.getSystem("IOEncoding"), 268 proc.readAllStandardOutput(),
266 'replace').strip() 269 Preferences.getSystem("IOEncoding"),
270 "replace",
271 ).strip()
267 try: 272 try:
268 jsonList = json.loads(output) 273 jsonList = json.loads(output)
269 except Exception: 274 except Exception:
270 jsonList = [] 275 jsonList = []
271 276
272 for package in jsonList: 277 for package in jsonList:
273 if isinstance(package, dict): 278 if isinstance(package, dict):
274 packages.append(( 279 packages.append(
275 package["name"], 280 (package["name"], package["version"], package["build_string"])
276 package["version"], 281 )
277 package["build_string"]
278 ))
279 else: 282 else:
280 parts = package.rsplit("-", 2) 283 parts = package.rsplit("-", 2)
281 while len(parts) < 3: 284 while len(parts) < 3:
282 parts.append("") 285 parts.append("")
283 packages.append(tuple(parts)) 286 packages.append(tuple(parts))
284 287
285 return packages 288 return packages
286 289
287 def getUpdateablePackages(self, name="", prefix=""): 290 def getUpdateablePackages(self, name="", prefix=""):
288 """ 291 """
289 Public method to get a list of updateable packages of a conda 292 Public method to get a list of updateable packages of a conda
290 environment. 293 environment.
291 294
292 @param name name of the environment 295 @param name name of the environment
293 @type str 296 @type str
294 @param prefix prefix of the environment 297 @param prefix prefix of the environment
295 @type str 298 @type str
296 @return list of installed packages. Each entry is a tuple containing 299 @return list of installed packages. Each entry is a tuple containing
297 the package name, version and build. 300 the package name, version and build.
298 @rtype list of tuples of (str, str, str) 301 @rtype list of tuples of (str, str, str)
299 @exception RuntimeError raised to indicate an error in parameters 302 @exception RuntimeError raised to indicate an error in parameters
300 303
301 Note: only one of name or prefix must be given. 304 Note: only one of name or prefix must be given.
302 """ 305 """
303 if name and prefix: 306 if name and prefix:
304 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 307 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
305 308
306 if not name and not prefix: 309 if not name and not prefix:
307 raise RuntimeError("One of 'name' or 'prefix' must be given.") 310 raise RuntimeError("One of 'name' or 'prefix' must be given.")
308 311
309 args = [ 312 args = [
310 "update", 313 "update",
311 "--json", 314 "--json",
312 "--quiet", 315 "--quiet",
313 "--all", 316 "--all",
315 ] 318 ]
316 if name: 319 if name:
317 args.extend(["--name", name]) 320 args.extend(["--name", name])
318 elif prefix: 321 elif prefix:
319 args.extend(["--prefix", prefix]) 322 args.extend(["--prefix", prefix])
320 323
321 exe = Preferences.getConda("CondaExecutable") 324 exe = Preferences.getConda("CondaExecutable")
322 if not exe: 325 if not exe:
323 exe = "conda" 326 exe = "conda"
324 327
325 packages = [] 328 packages = []
326 329
327 proc = QProcess() 330 proc = QProcess()
328 proc.start(exe, args) 331 proc.start(exe, args)
329 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 332 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
330 output = str(proc.readAllStandardOutput(), 333 output = str(
331 Preferences.getSystem("IOEncoding"), 334 proc.readAllStandardOutput(),
332 'replace').strip() 335 Preferences.getSystem("IOEncoding"),
336 "replace",
337 ).strip()
333 try: 338 try:
334 jsonDict = json.loads(output) 339 jsonDict = json.loads(output)
335 except Exception: 340 except Exception:
336 jsonDict = {} 341 jsonDict = {}
337 342
338 if "actions" in jsonDict and "LINK" in jsonDict["actions"]: 343 if "actions" in jsonDict and "LINK" in jsonDict["actions"]:
339 for linkEntry in jsonDict["actions"]["LINK"]: 344 for linkEntry in jsonDict["actions"]["LINK"]:
340 if isinstance(linkEntry, dict): 345 if isinstance(linkEntry, dict):
341 packages.append(( 346 packages.append(
342 linkEntry["name"], 347 (
343 linkEntry["version"], 348 linkEntry["name"],
344 linkEntry["build_string"] 349 linkEntry["version"],
345 )) 350 linkEntry["build_string"],
351 )
352 )
346 else: 353 else:
347 package = linkEntry.split()[0] 354 package = linkEntry.split()[0]
348 parts = package.rsplit("-", 2) 355 parts = package.rsplit("-", 2)
349 while len(parts) < 3: 356 while len(parts) < 3:
350 parts.append("") 357 parts.append("")
351 packages.append(tuple(parts)) 358 packages.append(tuple(parts))
352 359
353 return packages 360 return packages
354 361
355 def updatePackages(self, packages, name="", prefix=""): 362 def updatePackages(self, packages, name="", prefix=""):
356 """ 363 """
357 Public method to update packages of a conda environment. 364 Public method to update packages of a conda environment.
358 365
359 @param packages list of package names to be updated 366 @param packages list of package names to be updated
360 @type list of str 367 @type list of str
361 @param name name of the environment 368 @param name name of the environment
362 @type str 369 @type str
363 @param prefix prefix of the environment 370 @param prefix prefix of the environment
364 @type str 371 @type str
365 @return flag indicating success 372 @return flag indicating success
366 @rtype bool 373 @rtype bool
367 @exception RuntimeError raised to indicate an error in parameters 374 @exception RuntimeError raised to indicate an error in parameters
368 375
369 Note: only one of name or prefix must be given. 376 Note: only one of name or prefix must be given.
370 """ 377 """
371 if name and prefix: 378 if name and prefix:
372 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 379 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
373 380
374 if not name and not prefix: 381 if not name and not prefix:
375 raise RuntimeError("One of 'name' or 'prefix' must be given.") 382 raise RuntimeError("One of 'name' or 'prefix' must be given.")
376 383
377 if packages: 384 if packages:
378 args = [ 385 args = [
379 "update", 386 "update",
380 "--json", 387 "--json",
381 "--yes", 388 "--yes",
383 if name: 390 if name:
384 args.extend(["--name", name]) 391 args.extend(["--name", name])
385 elif prefix: 392 elif prefix:
386 args.extend(["--prefix", prefix]) 393 args.extend(["--prefix", prefix])
387 args.extend(packages) 394 args.extend(packages)
388 395
389 dlg = CondaExecDialog("update", self.__ui) 396 dlg = CondaExecDialog("update", self.__ui)
390 dlg.start(args) 397 dlg.start(args)
391 dlg.exec() 398 dlg.exec()
392 ok, _ = dlg.getResult() 399 ok, _ = dlg.getResult()
393 else: 400 else:
394 ok = False 401 ok = False
395 402
396 return ok 403 return ok
397 404
398 def updateAllPackages(self, name="", prefix=""): 405 def updateAllPackages(self, name="", prefix=""):
399 """ 406 """
400 Public method to update all packages of a conda environment. 407 Public method to update all packages of a conda environment.
401 408
402 @param name name of the environment 409 @param name name of the environment
403 @type str 410 @type str
404 @param prefix prefix of the environment 411 @param prefix prefix of the environment
405 @type str 412 @type str
406 @return flag indicating success 413 @return flag indicating success
407 @rtype bool 414 @rtype bool
408 @exception RuntimeError raised to indicate an error in parameters 415 @exception RuntimeError raised to indicate an error in parameters
409 416
410 Note: only one of name or prefix must be given. 417 Note: only one of name or prefix must be given.
411 """ 418 """
412 if name and prefix: 419 if name and prefix:
413 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 420 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
414 421
415 if not name and not prefix: 422 if not name and not prefix:
416 raise RuntimeError("One of 'name' or 'prefix' must be given.") 423 raise RuntimeError("One of 'name' or 'prefix' must be given.")
417 424
418 args = [ 425 args = ["update", "--json", "--yes", "--all"]
419 "update",
420 "--json",
421 "--yes",
422 "--all"
423 ]
424 if name: 426 if name:
425 args.extend(["--name", name]) 427 args.extend(["--name", name])
426 elif prefix: 428 elif prefix:
427 args.extend(["--prefix", prefix]) 429 args.extend(["--prefix", prefix])
428 430
429 dlg = CondaExecDialog("update", self.__ui) 431 dlg = CondaExecDialog("update", self.__ui)
430 dlg.start(args) 432 dlg.start(args)
431 dlg.exec() 433 dlg.exec()
432 ok, _ = dlg.getResult() 434 ok, _ = dlg.getResult()
433 435
434 return ok 436 return ok
435 437
436 def installPackages(self, packages, name="", prefix=""): 438 def installPackages(self, packages, name="", prefix=""):
437 """ 439 """
438 Public method to install packages into a conda environment. 440 Public method to install packages into a conda environment.
439 441
440 @param packages list of package names to be installed 442 @param packages list of package names to be installed
441 @type list of str 443 @type list of str
442 @param name name of the environment 444 @param name name of the environment
443 @type str 445 @type str
444 @param prefix prefix of the environment 446 @param prefix prefix of the environment
445 @type str 447 @type str
446 @return flag indicating success 448 @return flag indicating success
447 @rtype bool 449 @rtype bool
448 @exception RuntimeError raised to indicate an error in parameters 450 @exception RuntimeError raised to indicate an error in parameters
449 451
450 Note: only one of name or prefix must be given. 452 Note: only one of name or prefix must be given.
451 """ 453 """
452 if name and prefix: 454 if name and prefix:
453 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 455 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
454 456
455 if not name and not prefix: 457 if not name and not prefix:
456 raise RuntimeError("One of 'name' or 'prefix' must be given.") 458 raise RuntimeError("One of 'name' or 'prefix' must be given.")
457 459
458 if packages: 460 if packages:
459 args = [ 461 args = [
460 "install", 462 "install",
461 "--json", 463 "--json",
462 "--yes", 464 "--yes",
464 if name: 466 if name:
465 args.extend(["--name", name]) 467 args.extend(["--name", name])
466 elif prefix: 468 elif prefix:
467 args.extend(["--prefix", prefix]) 469 args.extend(["--prefix", prefix])
468 args.extend(packages) 470 args.extend(packages)
469 471
470 dlg = CondaExecDialog("install", self.__ui) 472 dlg = CondaExecDialog("install", self.__ui)
471 dlg.start(args) 473 dlg.start(args)
472 dlg.exec() 474 dlg.exec()
473 ok, _ = dlg.getResult() 475 ok, _ = dlg.getResult()
474 else: 476 else:
475 ok = False 477 ok = False
476 478
477 return ok 479 return ok
478 480
479 def uninstallPackages(self, packages, name="", prefix=""): 481 def uninstallPackages(self, packages, name="", prefix=""):
480 """ 482 """
481 Public method to uninstall packages of a conda environment (including 483 Public method to uninstall packages of a conda environment (including
482 all no longer needed dependencies). 484 all no longer needed dependencies).
483 485
484 @param packages list of package names to be uninstalled 486 @param packages list of package names to be uninstalled
485 @type list of str 487 @type list of str
486 @param name name of the environment 488 @param name name of the environment
487 @type str 489 @type str
488 @param prefix prefix of the environment 490 @param prefix prefix of the environment
489 @type str 491 @type str
490 @return flag indicating success 492 @return flag indicating success
491 @rtype bool 493 @rtype bool
492 @exception RuntimeError raised to indicate an error in parameters 494 @exception RuntimeError raised to indicate an error in parameters
493 495
494 Note: only one of name or prefix must be given. 496 Note: only one of name or prefix must be given.
495 """ 497 """
496 if name and prefix: 498 if name and prefix:
497 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 499 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
498 500
499 if not name and not prefix: 501 if not name and not prefix:
500 raise RuntimeError("One of 'name' or 'prefix' must be given.") 502 raise RuntimeError("One of 'name' or 'prefix' must be given.")
501 503
502 if packages: 504 if packages:
503 from UI.DeleteFilesConfirmationDialog import ( 505 from UI.DeleteFilesConfirmationDialog import DeleteFilesConfirmationDialog
504 DeleteFilesConfirmationDialog) 506
505 dlg = DeleteFilesConfirmationDialog( 507 dlg = DeleteFilesConfirmationDialog(
506 self.parent(), 508 self.parent(),
507 self.tr("Uninstall Packages"), 509 self.tr("Uninstall Packages"),
508 self.tr( 510 self.tr(
509 "Do you really want to uninstall these packages and" 511 "Do you really want to uninstall these packages and"
510 " their dependencies?"), 512 " their dependencies?"
511 packages) 513 ),
514 packages,
515 )
512 if dlg.exec() == QDialog.DialogCode.Accepted: 516 if dlg.exec() == QDialog.DialogCode.Accepted:
513 args = [ 517 args = [
514 "remove", 518 "remove",
515 "--json", 519 "--json",
516 "--yes", 520 "--yes",
517 ] 521 ]
518 if condaVersion() >= (4, 4, 0): 522 if condaVersion() >= (4, 4, 0):
519 args.append("--prune",) 523 args.append(
524 "--prune",
525 )
520 if name: 526 if name:
521 args.extend(["--name", name]) 527 args.extend(["--name", name])
522 elif prefix: 528 elif prefix:
523 args.extend(["--prefix", prefix]) 529 args.extend(["--prefix", prefix])
524 args.extend(packages) 530 args.extend(packages)
525 531
526 dlg = CondaExecDialog("remove", self.__ui) 532 dlg = CondaExecDialog("remove", self.__ui)
527 dlg.start(args) 533 dlg.start(args)
528 dlg.exec() 534 dlg.exec()
529 ok, _ = dlg.getResult() 535 ok, _ = dlg.getResult()
530 else: 536 else:
531 ok = False 537 ok = False
532 else: 538 else:
533 ok = False 539 ok = False
534 540
535 return ok 541 return ok
536 542
537 def searchPackages(self, pattern, fullNameOnly=False, packageSpec=False, 543 def searchPackages(
538 platform="", name="", prefix=""): 544 self,
545 pattern,
546 fullNameOnly=False,
547 packageSpec=False,
548 platform="",
549 name="",
550 prefix="",
551 ):
539 """ 552 """
540 Public method to search for a package pattern of a conda environment. 553 Public method to search for a package pattern of a conda environment.
541 554
542 @param pattern package search pattern 555 @param pattern package search pattern
543 @type str 556 @type str
544 @param fullNameOnly flag indicating to search for full names only 557 @param fullNameOnly flag indicating to search for full names only
545 @type bool 558 @type bool
546 @param packageSpec flag indicating to search a package specification 559 @param packageSpec flag indicating to search a package specification
554 @return flag indicating success and a dictionary with package name as 567 @return flag indicating success and a dictionary with package name as
555 key and list of dictionaries containing detailed data for the found 568 key and list of dictionaries containing detailed data for the found
556 packages as values 569 packages as values
557 @rtype tuple of (bool, dict of list of dict) 570 @rtype tuple of (bool, dict of list of dict)
558 @exception RuntimeError raised to indicate an error in parameters 571 @exception RuntimeError raised to indicate an error in parameters
559 572
560 Note: only one of name or prefix must be given. 573 Note: only one of name or prefix must be given.
561 """ 574 """
562 if name and prefix: 575 if name and prefix:
563 raise RuntimeError("Only one of 'name' or 'prefix' must be given.") 576 raise RuntimeError("Only one of 'name' or 'prefix' must be given.")
564 577
565 args = [ 578 args = [
566 "search", 579 "search",
567 "--json", 580 "--json",
568 ] 581 ]
569 if fullNameOnly: 582 if fullNameOnly:
575 if name: 588 if name:
576 args.extend(["--name", name]) 589 args.extend(["--name", name])
577 elif prefix: 590 elif prefix:
578 args.extend(["--prefix", prefix]) 591 args.extend(["--prefix", prefix])
579 args.append(pattern) 592 args.append(pattern)
580 593
581 exe = Preferences.getConda("CondaExecutable") 594 exe = Preferences.getConda("CondaExecutable")
582 if not exe: 595 if not exe:
583 exe = "conda" 596 exe = "conda"
584 597
585 packages = {} 598 packages = {}
586 ok = False 599 ok = False
587 600
588 proc = QProcess() 601 proc = QProcess()
589 proc.start(exe, args) 602 proc.start(exe, args)
590 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 603 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
591 output = str(proc.readAllStandardOutput(), 604 output = str(
592 Preferences.getSystem("IOEncoding"), 605 proc.readAllStandardOutput(),
593 'replace').strip() 606 Preferences.getSystem("IOEncoding"),
607 "replace",
608 ).strip()
594 with contextlib.suppress(Exception): 609 with contextlib.suppress(Exception):
595 packages = json.loads(output) 610 packages = json.loads(output)
596 ok = "error" not in packages 611 ok = "error" not in packages
597 612
598 return ok, packages 613 return ok, packages
599 614
600 ####################################################################### 615 #######################################################################
601 ## special methods below 616 ## special methods below
602 ####################################################################### 617 #######################################################################
603 618
604 def updateConda(self): 619 def updateConda(self):
605 """ 620 """
606 Public method to update conda itself. 621 Public method to update conda itself.
607 622
608 @return flag indicating success 623 @return flag indicating success
609 @rtype bool 624 @rtype bool
610 """ 625 """
611 args = [ 626 args = ["update", "--json", "--yes", "conda"]
612 "update", 627
613 "--json",
614 "--yes",
615 "conda"
616 ]
617
618 dlg = CondaExecDialog("update", self.__ui) 628 dlg = CondaExecDialog("update", self.__ui)
619 dlg.start(args) 629 dlg.start(args)
620 dlg.exec() 630 dlg.exec()
621 ok, _ = dlg.getResult() 631 ok, _ = dlg.getResult()
622 632
623 return ok 633 return ok
624 634
625 def writeDefaultConfiguration(self): 635 def writeDefaultConfiguration(self):
626 """ 636 """
627 Public method to create a conda configuration with default values. 637 Public method to create a conda configuration with default values.
628 """ 638 """
629 args = [ 639 args = ["config", "--write-default", "--quiet"]
630 "config", 640
631 "--write-default",
632 "--quiet"
633 ]
634
635 exe = Preferences.getConda("CondaExecutable") 641 exe = Preferences.getConda("CondaExecutable")
636 if not exe: 642 if not exe:
637 exe = "conda" 643 exe = "conda"
638 644
639 proc = QProcess() 645 proc = QProcess()
640 proc.start(exe, args) 646 proc.start(exe, args)
641 proc.waitForStarted(15000) 647 proc.waitForStarted(15000)
642 proc.waitForFinished(30000) 648 proc.waitForFinished(30000)
643 649
644 def getCondaInformation(self): 650 def getCondaInformation(self):
645 """ 651 """
646 Public method to get a dictionary containing information about conda. 652 Public method to get a dictionary containing information about conda.
647 653
648 @return dictionary containing information about conda 654 @return dictionary containing information about conda
649 @rtype dict 655 @rtype dict
650 """ 656 """
651 exe = Preferences.getConda("CondaExecutable") 657 exe = Preferences.getConda("CondaExecutable")
652 if not exe: 658 if not exe:
653 exe = "conda" 659 exe = "conda"
654 660
655 infoDict = {} 661 infoDict = {}
656 662
657 proc = QProcess() 663 proc = QProcess()
658 proc.start(exe, ["info", "--json"]) 664 proc.start(exe, ["info", "--json"])
659 if proc.waitForStarted(15000) and proc.waitForFinished(30000): 665 if proc.waitForStarted(15000) and proc.waitForFinished(30000):
660 output = str(proc.readAllStandardOutput(), 666 output = str(
661 Preferences.getSystem("IOEncoding"), 667 proc.readAllStandardOutput(),
662 'replace').strip() 668 Preferences.getSystem("IOEncoding"),
669 "replace",
670 ).strip()
663 try: 671 try:
664 infoDict = json.loads(output) 672 infoDict = json.loads(output)
665 except Exception: 673 except Exception:
666 infoDict = {} 674 infoDict = {}
667 675
668 return infoDict 676 return infoDict
669 677
670 def runProcess(self, args): 678 def runProcess(self, args):
671 """ 679 """
672 Public method to execute the conda with the given arguments. 680 Public method to execute the conda with the given arguments.
673 681
674 The conda executable is called with the given arguments and 682 The conda executable is called with the given arguments and
675 waited for its end. 683 waited for its end.
676 684
677 @param args list of command line arguments 685 @param args list of command line arguments
678 @type list of str 686 @type list of str
679 @return tuple containing a flag indicating success and the output 687 @return tuple containing a flag indicating success and the output
680 of the process 688 of the process
681 @rtype tuple of (bool, str) 689 @rtype tuple of (bool, str)
682 """ 690 """
683 exe = Preferences.getConda("CondaExecutable") 691 exe = Preferences.getConda("CondaExecutable")
684 if not exe: 692 if not exe:
685 exe = "conda" 693 exe = "conda"
686 694
687 process = QProcess() 695 process = QProcess()
688 process.start(exe, args) 696 process.start(exe, args)
689 procStarted = process.waitForStarted(15000) 697 procStarted = process.waitForStarted(15000)
690 if procStarted: 698 if procStarted:
691 finished = process.waitForFinished(30000) 699 finished = process.waitForFinished(30000)
692 if finished: 700 if finished:
693 if process.exitCode() == 0: 701 if process.exitCode() == 0:
694 output = str(process.readAllStandardOutput(), 702 output = str(
695 Preferences.getSystem("IOEncoding"), 703 process.readAllStandardOutput(),
696 'replace').strip() 704 Preferences.getSystem("IOEncoding"),
705 "replace",
706 ).strip()
697 return True, output 707 return True, output
698 else: 708 else:
699 return (False, 709 return (
700 self.tr("conda exited with an error ({0}).") 710 False,
701 .format(process.exitCode())) 711 self.tr("conda exited with an error ({0}).").format(
712 process.exitCode()
713 ),
714 )
702 else: 715 else:
703 process.terminate() 716 process.terminate()
704 process.waitForFinished(2000) 717 process.waitForFinished(2000)
705 process.kill() 718 process.kill()
706 process.waitForFinished(3000) 719 process.waitForFinished(3000)
707 return False, self.tr("conda did not finish within" 720 return False, self.tr("conda did not finish within" " 3 seconds.")
708 " 3 seconds.") 721
709
710 return False, self.tr("conda could not be started.") 722 return False, self.tr("conda could not be started.")
711 723
712 def cleanConda(self, cleanAction): 724 def cleanConda(self, cleanAction):
713 """ 725 """
714 Public method to update conda itself. 726 Public method to update conda itself.
715 727
716 @param cleanAction cleaning action to be performed (must be one of 728 @param cleanAction cleaning action to be performed (must be one of
717 the command line parameters without '--') 729 the command line parameters without '--')
718 @type str 730 @type str
719 """ 731 """
720 args = [ 732 args = [
721 "clean", 733 "clean",
722 "--yes", 734 "--yes",
723 "--{0}".format(cleanAction), 735 "--{0}".format(cleanAction),
724 ] 736 ]
725 737
726 dlg = CondaExecDialog("clean", self.__ui) 738 dlg = CondaExecDialog("clean", self.__ui)
727 dlg.start(args) 739 dlg.start(args)
728 dlg.exec() 740 dlg.exec()

eric ide

mercurial