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 |
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() |