VirtualEnv/VirtualenvExecDialog.py

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

eric ide

mercurial