ProjectFlask/Project.py

changeset 2
6cc80e4db8a7
child 3
265c3c2914e2
equal deleted inserted replaced
1:d6557db39585 2:6cc80e4db8a7
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2020 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the Flask project support.
8 """
9
10 import os
11
12 from PyQt5.QtCore import pyqtSlot, QObject, QProcess, QTimer
13 from PyQt5.QtWidgets import QMenu
14
15 from E5Gui import E5MessageBox
16 from E5Gui.E5Action import E5Action
17 from E5Gui.E5Application import e5App
18
19 from Globals import isWindowsPlatform
20
21 import UI.PixmapCache
22 import Utilities
23
24
25 class Project(QObject):
26 """
27 Class implementing the Flask project support.
28 """
29 def __init__(self, plugin, iconSuffix, parent=None):
30 """
31 Constructor
32
33 @param plugin reference to the plugin object
34 @type ProjectFlaskPlugin
35 @param iconSuffix suffix for the icons
36 @type str
37 @param parent parent
38 @type QObject
39 """
40 super(Project, self).__init__(parent)
41
42 self.__plugin = plugin
43 self.__iconSuffix = iconSuffix
44 self.__ui = parent
45
46 self.__e5project = e5App().getObject("Project")
47 self.__virtualEnvManager = e5App().getObject("VirtualEnvManager")
48
49 self.__menus = {} # dictionary with references to menus
50
51 self.__serverProc = None
52
53 self.__flaskVersions = {
54 "python": "",
55 "flask": "",
56 "werkzeug": "",
57 }
58
59 def initActions(self):
60 """
61 Public method to define the Flask actions.
62 """
63 self.actions = []
64
65 ##############################
66 ## about action below ##
67 ##############################
68
69 self.aboutFlaskAct = E5Action(
70 self.tr('About Flask'),
71 self.tr('About &Flask'),
72 0, 0,
73 self, 'flask_about')
74 self.aboutFlaskAct.setStatusTip(self.tr(
75 'Shows some information about Flask'))
76 self.aboutFlaskAct.setWhatsThis(self.tr(
77 """<b>About Flask</b>"""
78 """<p>Shows some information about Flask.</p>"""
79 ))
80 self.aboutFlaskAct.triggered.connect(self.__flaskInfo)
81 self.actions.append(self.aboutFlaskAct)
82
83 def initMenu(self):
84 """
85 Public method to initialize the Flask menu.
86
87 @return the menu generated
88 @rtype QMenu
89 """
90 self.__menus = {} # clear menus references
91
92 menu = QMenu(self.tr('&Flask'), self.__ui)
93 menu.setTearOffEnabled(True)
94
95 menu.addAction(self.aboutFlaskAct)
96
97 self.__menus["main"] = menu
98
99 return menu
100
101 def getMenu(self, name):
102 """
103 Public method to get a reference to the requested menu.
104
105 @param name name of the menu
106 @type str
107 @return reference to the menu or None, if no menu with the given
108 name exists
109 @rtype QMenu or None
110 """
111 if name in self.__menus:
112 return self.__menus[name]
113 else:
114 return None
115
116 def getMenuNames(self):
117 """
118 Public method to get the names of all menus.
119
120 @return menu names
121 @rtype list of str
122 """
123 return list(self.__menus.keys())
124
125 ##################################################################
126 ## slots below implement general functionality
127 ##################################################################
128
129 def projectClosed(self):
130 """
131 Public method to handle the closing of a project.
132 """
133 if self.__serverProc is not None:
134 self.__serverProcFinished()
135
136 # TODO: implement this correctly
137 def supportedPythonVariants(self):
138 """
139 Public method to get the supported Python variants.
140
141 @return list of supported Python variants
142 @rtype list of str
143 """
144 variants = []
145
146 virtEnv = self.__getVirtualEnvironment()
147 if virtEnv:
148 fullCmd = self.getFlaskCommand()
149 if fullCmd:
150 variants.append("Python3")
151 else:
152 fullCmd = self.getFlaskCommand()
153 if isWindowsPlatform():
154 if fullCmd:
155 variants.append("Python3")
156 else:
157 fullCmds = Utilities.getExecutablePaths("flask")
158 for fullCmd in fullCmds:
159 try:
160 with open(fullCmd, 'r', encoding='utf-8') as f:
161 l0 = f.readline()
162 except (IOError, OSError):
163 l0 = ""
164 if self.__isSuitableForVariant("Python3", l0):
165 variants.append("Python3")
166 break
167
168 return variants
169
170 def __isSuitableForVariant(self, variant, line0):
171 """
172 Private method to test, if a detected command file is suitable for the
173 given Python variant.
174
175 @param variant Python variant to test for
176 @type str
177 @param line0 first line of the executable
178 @type str
179 @return flag indicating a suitable file was found
180 @rtype bool
181 """
182 l0 = line0.lower()
183 ok = (variant.lower() in l0 or
184 "{0}.".format(variant[-1]) in l0)
185 ok |= "pypy3" in l0
186
187 return ok
188
189 def __getVirtualEnvironment(self):
190 """
191 Private method to get the path of the virtual environment.
192
193 @return path of the virtual environment
194 @rtype str
195 """
196 language = self.__e5project.getProjectLanguage()
197 if language == "Python3":
198 venvName = self.__plugin.getPreferences(
199 "VirtualEnvironmentNamePy3")
200 else:
201 venvName = ""
202 if venvName:
203 virtEnv = self.__virtualEnvManager.getVirtualenvDirectory(
204 venvName)
205 else:
206 virtEnv = ""
207
208 if virtEnv and not os.path.exists(virtEnv):
209 virtEnv = ""
210
211 return virtEnv # __IGNORE_WARNING_M834__
212
213 def getFlaskCommand(self):
214 """
215 Public method to build the Flask command.
216
217 @return full flask command
218 @rtype str
219 """
220 cmd = "flask"
221
222 virtualEnv = self.__getVirtualEnvironment()
223 if isWindowsPlatform():
224 fullCmds = [
225 os.path.join(virtualEnv, "Scripts", cmd + '.exe'),
226 os.path.join(virtualEnv, "bin", cmd + '.exe'),
227 cmd # fall back to just cmd
228 ]
229 for cmd in fullCmds:
230 if os.path.exists(cmd):
231 break
232 else:
233 fullCmds = [
234 os.path.join(virtualEnv, "bin", cmd),
235 os.path.join(virtualEnv, "local", "bin", cmd),
236 Utilities.getExecutablePath(cmd),
237 cmd # fall back to just cmd
238 ]
239 for cmd in fullCmds:
240 if os.path.exists(cmd):
241 break
242 return cmd
243
244 @pyqtSlot()
245 def __flaskInfo(self):
246 """
247 Private slot to show some info about Flask.
248 """
249 versions = self.getFlaskVersionStrings()
250 url = "https://flask.palletsprojects.com"
251
252 msgBox = E5MessageBox.E5MessageBox(
253 E5MessageBox.Question,
254 self.tr("About Flask"),
255 self.tr(
256 "<p>Flask is a lightweight WSGI web application framework."
257 " It is designed to make getting started quick and easy,"
258 " with the ability to scale up to complex applications.</p>"
259 "<p><table>"
260 "<tr><td>Flask Version:</td><td>{0}</td></tr>"
261 "<tr><td>Werkzeug Version:</td><td>{1}</td></tr>"
262 "<tr><td>Python Version:</td><td>{2}</td></tr>"
263 "<tr><td>Flask URL:</td><td><a href=\"{3}\">"
264 "{3}</a></td></tr>"
265 "</table></p>"
266 ).format(versions["flask"], versions["werkzeug"],
267 versions["python"], url),
268 modal=True,
269 buttons=E5MessageBox.Ok)
270 msgBox.setIconPixmap(UI.PixmapCache.getPixmap(
271 os.path.join("ProjectFlask", "icons",
272 "flask64-{0}".format(self.__iconSuffix))))
273 msgBox.exec()
274
275 def getFlaskVersionStrings(self):
276 """
277 Public method to get the Flask, Werkzeug and Python versions as a
278 string.
279
280 @return dictionary containing the Flask, Werkzeug and Python versions
281 @rtype dict
282 """
283 if not self.__flaskVersions["flask"]:
284 cmd = self.getFlaskCommand()
285 proc = QProcess()
286 proc.start(cmd, ["--version"])
287 if proc.waitForFinished(10000):
288 output = str(proc.readAllStandardOutput(), "utf-8")
289 for line in output.lower().splitlines():
290 key, version = line.strip().split(None, 1)
291 self.__flaskVersions[key] = version
292
293 return self.__flaskVersions
294
295 ##################################################################
296 ## slots below implement run functions
297 ##################################################################
298
299 def __runServer(self, logging=False):
300 """
301 Private slot to start the Pyramid Web server.
302
303 @param logging flag indicating to enable logging
304 @type bool
305 """
306 # TODO: implement this
307
308 def __serverProcFinished(self):
309 """
310 Private slot connected to the finished signal.
311 """
312 if (
313 self.__serverProc is not None and
314 self.__serverProc.state() != QProcess.NotRunning
315 ):
316 self.__serverProc.terminate()
317 QTimer.singleShot(2000, self.__serverProc.kill)
318 self.__serverProc.waitForFinished(3000)
319 self.__serverProc = None

eric ide

mercurial