Plugins/UiExtensionPlugins/VirtualenvInterface/VirtualenvExecDialog.py

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

eric ide

mercurial