src/eric7/VirtualEnv/VirtualenvUpgradeExecDialog.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 9144
135240382a3e
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the virtualenv upgrade execution dialog.
8 """
9
10 import os
11
12 from PyQt6.QtCore import QProcess, QTimer
13 from PyQt6.QtWidgets import QDialog, QDialogButtonBox
14
15 from .Ui_VirtualenvExecDialog import Ui_VirtualenvExecDialog
16
17 from Globals import getPythonExecutable
18 import Preferences
19
20
21 class VirtualenvUpgradeExecDialog(QDialog, Ui_VirtualenvExecDialog):
22 """
23 Class implementing the virtualenv upgrade execution dialog.
24 """
25 def __init__(self, venvName, interpreter, createLog, venvManager,
26 parent=None):
27 """
28 Constructor
29
30 @param venvName name of the virtual environment to be upgraded
31 @type str
32 @param interpreter interpreter to be used for the upgrade
33 @type str
34 @param createLog flag indicating to create a log file for the upgrade
35 @type bool
36 @param venvManager reference to the virtual environment manager
37 @type VirtualenvManager
38 @param parent reference to the parent widget
39 @type QWidget
40 """
41 super().__init__(parent)
42 self.setupUi(self)
43
44 self.buttonBox.button(
45 QDialogButtonBox.StandardButton.Close).setEnabled(False)
46 self.buttonBox.button(
47 QDialogButtonBox.StandardButton.Cancel).setDefault(True)
48
49 self.__process = None
50 self.__cmd = ""
51
52 self.__progs = []
53 if interpreter:
54 self.__progs.append(interpreter)
55 self.__progs.extend([
56 getPythonExecutable(),
57 "python3",
58 "python",
59 ])
60 self.__callIndex = 0
61 self.__callArgs = []
62
63 self.__venvName = venvName
64 self.__venvDirectory = ""
65 self.__createLog = createLog
66 self.__manager = venvManager
67
68 def start(self, arguments):
69 """
70 Public slot to start the virtualenv command.
71
72 @param arguments commandline arguments for virtualenv/pyvenv program
73 (list of strings)
74 """
75 if self.__callIndex == 0:
76 # first attempt, add a given python interpreter and do
77 # some other setup
78 self.errorGroup.hide()
79 self.contents.clear()
80 self.errors.clear()
81
82 self.__process = QProcess()
83 self.__process.readyReadStandardOutput.connect(self.__readStdout)
84 self.__process.readyReadStandardError.connect(self.__readStderr)
85 self.__process.finished.connect(self.__finish)
86
87 self.__callArgs = arguments
88 self.__venvDirectory = arguments[-1]
89
90 prog = self.__progs[self.__callIndex]
91 self.__cmd = "{0} {1}".format(prog, " ".join(arguments))
92 self.__logOutput(self.tr("Executing: {0}\n").format(
93 self.__cmd))
94 self.__process.start(prog, arguments)
95 procStarted = self.__process.waitForStarted(5000)
96 if not procStarted:
97 self.__logOutput(self.tr("Failed\n\n"))
98 self.__nextAttempt()
99
100 def on_buttonBox_clicked(self, button):
101 """
102 Private slot called by a button of the button box clicked.
103
104 @param button button that was clicked (QAbstractButton)
105 """
106 if button == self.buttonBox.button(
107 QDialogButtonBox.StandardButton.Close
108 ):
109 self.accept()
110 elif button == self.buttonBox.button(
111 QDialogButtonBox.StandardButton.Cancel
112 ):
113 self.__finish(0, 0, giveUp=True)
114
115 def __finish(self, exitCode, exitStatus, giveUp=False):
116 """
117 Private slot called when the process finished.
118
119 It is called when the process finished or
120 the user pressed the button.
121
122 @param exitCode exit code of the process (integer)
123 @param exitStatus exit status of the process (QProcess.ExitStatus)
124 @param giveUp flag indicating to not start another attempt (boolean)
125 """
126 if (
127 self.__process is not None and
128 self.__process.state() != QProcess.ProcessState.NotRunning
129 ):
130 self.__process.terminate()
131 QTimer.singleShot(2000, self.__process.kill)
132 self.__process.waitForFinished(3000)
133
134 self.buttonBox.button(
135 QDialogButtonBox.StandardButton.Close).setEnabled(True)
136 self.buttonBox.button(
137 QDialogButtonBox.StandardButton.Cancel).setEnabled(False)
138 self.buttonBox.button(
139 QDialogButtonBox.StandardButton.Close).setDefault(True)
140
141 if not giveUp:
142 if exitCode != 0:
143 self.__logOutput(self.tr("Failed\n\n"))
144 if len(self.errors.toPlainText().splitlines()) == 1:
145 self.errors.clear()
146 self.errorGroup.hide()
147 self.__nextAttempt()
148 return
149
150 self.__process = None
151
152 self.__logOutput(self.tr('\npyvenv finished.\n'))
153
154 if self.__createLog:
155 self.__writeLogFile()
156
157 self.__changeVirtualEnvironmentInterpreter()
158
159 def __nextAttempt(self):
160 """
161 Private method to start another attempt.
162 """
163 self.__callIndex += 1
164 if self.__callIndex < len(self.__progs):
165 self.start(self.__callArgs)
166 else:
167 self.__logError(
168 self.tr('No suitable pyvenv program could be'
169 ' started.\n'))
170 self.__cmd = ""
171 self.__finish(0, 0, giveUp=True)
172
173 def __readStdout(self):
174 """
175 Private slot to handle the readyReadStandardOutput signal.
176
177 It reads the output of the process, formats it and inserts it into
178 the contents pane.
179 """
180 self.__process.setReadChannel(QProcess.ProcessChannel.StandardOutput)
181
182 while self.__process.canReadLine():
183 s = str(self.__process.readLine(),
184 Preferences.getSystem("IOEncoding"),
185 'replace')
186 self.__logOutput(s)
187
188 def __readStderr(self):
189 """
190 Private slot to handle the readyReadStandardError signal.
191
192 It reads the error output of the process and inserts it into the
193 error pane.
194 """
195 self.__process.setReadChannel(QProcess.ProcessChannel.StandardError)
196
197 while self.__process.canReadLine():
198 s = str(self.__process.readLine(),
199 Preferences.getSystem("IOEncoding"),
200 'replace')
201 self.__logError(s)
202
203 def __logOutput(self, s):
204 """
205 Private method to log some output.
206
207 @param s output string to log (string)
208 """
209 self.contents.insertPlainText(s)
210 self.contents.ensureCursorVisible()
211
212 def __logError(self, s):
213 """
214 Private method to log an error.
215
216 @param s error string to log (string)
217 """
218 self.errorGroup.show()
219 self.errors.insertPlainText(s)
220 self.errors.ensureCursorVisible()
221
222 def __writeLogFile(self):
223 """
224 Private method to write a log file to the virtualenv directory.
225 """
226 outtxt = self.contents.toPlainText()
227 logFile = os.path.join(self.__venvDirectory, "pyvenv_upgrade.log")
228 self.__logOutput(self.tr("\nWriting log file '{0}'.\n")
229 .format(logFile))
230
231 try:
232 with open(logFile, "w", encoding="utf-8") as f:
233 f.write(self.tr("Output:\n"))
234 f.write(outtxt)
235 errtxt = self.errors.toPlainText()
236 if errtxt:
237 f.write("\n")
238 f.write(self.tr("Errors:\n"))
239 f.write(errtxt)
240 except OSError as err:
241 self.__logError(
242 self.tr("""The logfile '{0}' could not be written.\n"""
243 """Reason: {1}\n""").format(logFile, str(err)))
244 self.__logOutput(self.tr("Done.\n"))
245
246 def __changeVirtualEnvironmentInterpreter(self):
247 """
248 Private method to change the interpreter of the upgraded virtual
249 environment.
250 """
251 from .VirtualenvInterpreterSelectionDialog import (
252 VirtualenvInterpreterSelectionDialog
253 )
254
255 venvInterpreter = ""
256 dlg = VirtualenvInterpreterSelectionDialog(
257 self.__venvName, self.__venvDirectory)
258 if dlg.exec() == QDialog.DialogCode.Accepted:
259 venvInterpreter = dlg.getData()
260
261 if venvInterpreter:
262 self.__manager.setVirtualEnvInterpreter(
263 self.__venvName, venvInterpreter)

eric ide

mercurial