eric6/VirtualEnv/VirtualenvExecDialog.py

changeset 6942
2602857055c5
parent 6696
706185900558
child 7192
a22eee00b052
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the virtualenv execution dialog.
8 """
9
10 from __future__ import unicode_literals
11 try:
12 str = unicode
13 except NameError:
14 pass
15
16 import sys
17 import os
18
19 from PyQt5.QtCore import QProcess, QTimer, QUrl
20 from PyQt5.QtGui import QDesktopServices
21 from PyQt5.QtWidgets import QDialog, QDialogButtonBox
22
23 from .Ui_VirtualenvExecDialog import Ui_VirtualenvExecDialog
24
25 import Preferences
26 from Globals import isWindowsPlatform
27
28
29 class VirtualenvExecDialog(QDialog, Ui_VirtualenvExecDialog):
30 """
31 Class implementing the virtualenv execution dialog.
32
33 This class starts a QProcess and displays a dialog that
34 shows the output of the virtualenv or pyvenv process.
35 """
36 def __init__(self, configuration, venvManager, parent=None):
37 """
38 Constructor
39
40 @param configuration dictionary containing the configuration parameters
41 as returned by the command configuration dialog
42 @type dict
43 @param venvManager reference to the virtual environment manager
44 @type VirtualenvManager
45 @param parent reference to the parent widget
46 @type QWidget
47 """
48 super(VirtualenvExecDialog, self).__init__(parent)
49 self.setupUi(self)
50
51 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
52 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
53
54 self.__pyvenv = configuration["envType"] == "pyvenv"
55 self.__targetDir = configuration["targetDirectory"]
56 self.__openTarget = configuration["openTarget"]
57 self.__createLog = configuration["createLog"]
58 self.__createScript = configuration["createScript"]
59 self.__venvName = configuration["logicalName"]
60 self.__venvManager = venvManager
61
62 self.__process = None
63 self.__cmd = ""
64
65 if self.__pyvenv:
66 self.__calls = []
67 if configuration["pythonExe"]:
68 self.__calls.append((configuration["pythonExe"],
69 ["-m", "venv"]))
70 self.__calls.extend([
71 (sys.executable.replace("w.exe", ".exe"),
72 ["-m", "venv"]),
73 ("python3", ["-m", "venv"]),
74 ("python", ["-m", "venv"]),
75 ])
76 else:
77 self.__calls = [
78 (sys.executable.replace("w.exe", ".exe"),
79 ["-m", "virtualenv"]),
80 ("virtualenv", []),
81 ]
82 self.__callIndex = 0
83 self.__callArgs = []
84
85 def start(self, arguments):
86 """
87 Public slot to start the virtualenv command.
88
89 @param arguments commandline arguments for virtualenv/pyvenv program
90 (list of strings)
91 """
92 if self.__callIndex == 0:
93 # first attempt, add a given python interpreter and do
94 # some other setup
95 self.errorGroup.hide()
96 self.contents.clear()
97 self.errors.clear()
98
99 self.__process = QProcess()
100 self.__process.readyReadStandardOutput.connect(self.__readStdout)
101 self.__process.readyReadStandardError.connect(self.__readStderr)
102 self.__process.finished.connect(self.__finish)
103
104 if not self.__pyvenv:
105 for arg in arguments:
106 if arg.startswith("--python="):
107 prog = arg.replace("--python=", "")
108 self.__calls.insert(
109 0, (prog, ["-m", "virtualenv"]))
110 break
111 self.__callArgs = arguments
112
113 prog, args = self.__calls[self.__callIndex]
114 args.extend(self.__callArgs)
115 self.__cmd = "{0} {1}".format(prog, " ".join(args))
116 self.__logOutput(self.tr("Executing: {0}\n").format(
117 self.__cmd))
118 self.__process.start(prog, args)
119 procStarted = self.__process.waitForStarted(5000)
120 if not procStarted:
121 self.__logOutput(self.tr("Failed\n\n"))
122 self.__nextAttempt()
123
124 def on_buttonBox_clicked(self, button):
125 """
126 Private slot called by a button of the button box clicked.
127
128 @param button button that was clicked (QAbstractButton)
129 """
130 if button == self.buttonBox.button(QDialogButtonBox.Close):
131 self.accept()
132 elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
133 self.__finish(0, 0, giveUp=True)
134
135 def __finish(self, exitCode, exitStatus, giveUp=False):
136 """
137 Private slot called when the process finished.
138
139 It is called when the process finished or
140 the user pressed the button.
141
142 @param exitCode exit code of the process (integer)
143 @param exitStatus exit status of the process (QProcess.ExitStatus)
144 @keyparam giveUp flag indicating to not start another attempt (boolean)
145 """
146 if self.__process is not None and \
147 self.__process.state() != QProcess.NotRunning:
148 self.__process.terminate()
149 QTimer.singleShot(2000, self.__process.kill)
150 self.__process.waitForFinished(3000)
151
152 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
153 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
154 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
155
156 if not giveUp:
157 if exitCode != 0:
158 self.__logOutput(self.tr("Failed\n\n"))
159 if len(self.errors.toPlainText().splitlines()) == 1:
160 self.errors.clear()
161 self.errorGroup.hide()
162 self.__nextAttempt()
163 return
164
165 self.__process = None
166
167 if self.__pyvenv:
168 self.__logOutput(self.tr('\npyvenv finished.\n'))
169 else:
170 self.__logOutput(self.tr('\nvirtualenv finished.\n'))
171
172 if os.path.exists(self.__targetDir):
173 if self.__createScript:
174 self.__writeScriptFile()
175
176 if self.__createLog:
177 self.__writeLogFile()
178
179 if self.__openTarget:
180 QDesktopServices.openUrl(QUrl.fromLocalFile(
181 self.__targetDir))
182
183 self.__venvManager.addVirtualEnv(self.__venvName,
184 self.__targetDir)
185
186 def __nextAttempt(self):
187 """
188 Private method to start another attempt.
189 """
190 self.__callIndex += 1
191 if self.__callIndex < len(self.__calls):
192 self.start(self.__callArgs)
193 else:
194 if self.__pyvenv:
195 self.__logError(
196 self.tr('No suitable pyvenv program could be'
197 ' started.\n'))
198 else:
199 self.__logError(
200 self.tr('No suitable virtualenv program could be'
201 ' started.\n'))
202 self.__cmd = ""
203 self.__finish(0, 0, giveUp=True)
204
205 def __readStdout(self):
206 """
207 Private slot to handle the readyReadStandardOutput signal.
208
209 It reads the output of the process, formats it and inserts it into
210 the contents pane.
211 """
212 self.__process.setReadChannel(QProcess.StandardOutput)
213
214 while self.__process.canReadLine():
215 s = str(self.__process.readLine(),
216 Preferences.getSystem("IOEncoding"),
217 'replace')
218 self.__logOutput(s)
219
220 def __readStderr(self):
221 """
222 Private slot to handle the readyReadStandardError signal.
223
224 It reads the error output of the process and inserts it into the
225 error pane.
226 """
227 self.__process.setReadChannel(QProcess.StandardError)
228
229 while self.__process.canReadLine():
230 s = str(self.__process.readLine(),
231 Preferences.getSystem("IOEncoding"),
232 'replace')
233 self.__logError(s)
234
235 def __logOutput(self, s):
236 """
237 Private method to log some output.
238
239 @param s output string to log (string)
240 """
241 self.contents.insertPlainText(s)
242 self.contents.ensureCursorVisible()
243
244 def __logError(self, s):
245 """
246 Private method to log an error.
247
248 @param s error string to log (string)
249 """
250 self.errorGroup.show()
251 self.errors.insertPlainText(s)
252 self.errors.ensureCursorVisible()
253
254 def __writeLogFile(self):
255 """
256 Private method to write a log file to the virtualenv directory.
257 """
258 outtxt = self.contents.toPlainText()
259 if self.__pyvenv:
260 logFile = os.path.join(self.__targetDir, "pyvenv.log")
261 else:
262 logFile = os.path.join(self.__targetDir, "virtualenv.log")
263 self.__logOutput(self.tr("\nWriting log file '{0}'.\n")
264 .format(logFile))
265
266 try:
267 f = open(logFile, "w", encoding="utf-8")
268 f.write(self.tr("Output:\n"))
269 f.write(outtxt)
270 errtxt = self.errors.toPlainText()
271 if errtxt:
272 f.write("\n")
273 f.write(self.tr("Errors:\n"))
274 f.write(errtxt)
275 f.close()
276 except (IOError, OSError) as err:
277 self.__logError(
278 self.tr("""The logfile '{0}' could not be written.\n"""
279 """Reason: {1}\n""").format(logFile, str(err)))
280 self.__logOutput(self.tr("Done.\n"))
281
282 def __writeScriptFile(self):
283 """
284 Private method to write a script file to the virtualenv directory.
285 """
286 if self.__pyvenv:
287 basename = "create_pyvenv"
288 else:
289 basename = "create_virtualenv"
290 if isWindowsPlatform():
291 script = os.path.join(self.__targetDir, basename + ".cmd")
292 txt = self.__cmd
293 else:
294 script = os.path.join(self.__targetDir, basename + ".sh")
295 txt = "#!/usr/bin/env sh\n\n" + self.__cmd
296
297 self.__logOutput(self.tr("\nWriting script file '{0}'.\n")
298 .format(script))
299
300 try:
301 f = open(script, "w", encoding="utf-8")
302 f.write(txt)
303 f.close()
304 except (IOError, OSError) as err:
305 self.__logError(
306 self.tr("""The script file '{0}' could not be written.\n"""
307 """Reason: {1}\n""").format(script, str(err)))
308 self.__logOutput(self.tr("Done.\n"))

eric ide

mercurial