src/eric7/VirtualEnv/VirtualenvExecDialog.py

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

eric ide

mercurial