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