|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2018 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the PyInstaller interface plug-in. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 import os |
|
13 import platform |
|
14 |
|
15 from PyQt5.QtCore import QObject, QCoreApplication |
|
16 |
|
17 import Utilities |
|
18 |
|
19 # Start-Of-Header |
|
20 name = "PyInstaller Plugin" |
|
21 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
|
22 autoactivate = True |
|
23 deactivateable = True |
|
24 version = "0.1.0" |
|
25 className = "PyInstallerPlugin" |
|
26 packageName = "PyInstaller" |
|
27 shortDescription = "Show dialogs to configure and execute PyInstaller." |
|
28 longDescription = ( |
|
29 """This plug-in implements dialogs to configure and execute PyInstaller""" |
|
30 """ for an eric project. PyInstaller must be available or must be""" |
|
31 """ installed via 'pip install PyInstaller'.""" |
|
32 ) |
|
33 needsRestart = False |
|
34 pyqtApi = 2 |
|
35 python2Compatible = True |
|
36 # End-Of-Header |
|
37 |
|
38 error = "" |
|
39 |
|
40 exePy2 = [] |
|
41 exePy3 = [] |
|
42 |
|
43 |
|
44 def exeDisplayDataList(): |
|
45 """ |
|
46 Module function to support the display of some executable info. |
|
47 |
|
48 @return list of dictionaries containing the data to query the presence of |
|
49 the executable |
|
50 @rtype list of dict |
|
51 """ |
|
52 dataList = [] |
|
53 data = { |
|
54 "programEntry": True, |
|
55 "header": QCoreApplication.translate( |
|
56 "PyInstallerPlugin", "Packagers - PyInstaller"), |
|
57 "exe": "dummyExe", |
|
58 "versionCommand": "--version", |
|
59 "versionStartsWith": "dummyExe", |
|
60 "versionPosition": -1, |
|
61 "version": "", |
|
62 "versionCleanup": None, |
|
63 "versionRe": "^\d", |
|
64 } |
|
65 |
|
66 if _checkProgram(): |
|
67 for exePath in (exePy2 + exePy3): |
|
68 data["exe"] = exePath |
|
69 data["versionStartsWith"] = "" |
|
70 dataList.append(data.copy()) |
|
71 else: |
|
72 dataList.append(data) |
|
73 return dataList |
|
74 |
|
75 |
|
76 def _findExecutable(majorVersion): |
|
77 """ |
|
78 Restricted function to determine the names of the executables. |
|
79 |
|
80 @param majorVersion major python version |
|
81 @type int |
|
82 @return names of the executables |
|
83 @rtype list of str |
|
84 """ |
|
85 # Determine Python Version |
|
86 if majorVersion == 3: |
|
87 minorVersions = range(10) |
|
88 elif majorVersion == 2: |
|
89 minorVersions = [7] # PyInstaller supports just Python 2.7 |
|
90 else: |
|
91 return [] |
|
92 |
|
93 executables = set() |
|
94 if Utilities.isWindowsPlatform(): |
|
95 # |
|
96 # Windows |
|
97 # |
|
98 try: |
|
99 import winreg |
|
100 except ImportError: |
|
101 import _winreg as winreg # __IGNORE_WARNING__ |
|
102 |
|
103 def getExePath(branch, access, versionStr): |
|
104 exes = [] |
|
105 try: |
|
106 software = winreg.OpenKey(branch, 'Software', 0, access) |
|
107 python = winreg.OpenKey(software, 'Python', 0, access) |
|
108 pcore = winreg.OpenKey(python, 'PythonCore', 0, access) |
|
109 version = winreg.OpenKey(pcore, versionStr, 0, access) |
|
110 installpath = winreg.QueryValue(version, 'InstallPath') |
|
111 # Look for pyinstaller.exe |
|
112 exe = os.path.join(installpath, 'Scripts', 'pyinstaller.exe') |
|
113 if os.access(exe, os.X_OK): |
|
114 exes.append(exe) |
|
115 # Look for pyi-makespec.exe |
|
116 exe = os.path.join(installpath, 'Scripts', 'pyi-makespec.exe') |
|
117 if os.access(exe, os.X_OK): |
|
118 exes.append(exe) |
|
119 except (WindowsError, OSError): # __IGNORE_WARNING__ |
|
120 pass |
|
121 return exes |
|
122 |
|
123 for minorVersion in minorVersions: |
|
124 versionStr = '{0}.{1}'.format(majorVersion, minorVersion) |
|
125 exePath = getExePath( |
|
126 winreg.HKEY_CURRENT_USER, |
|
127 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
|
128 if exePath is not None: |
|
129 executables.add(exePath) |
|
130 |
|
131 exePath = getExePath( |
|
132 winreg.HKEY_LOCAL_MACHINE, |
|
133 winreg.KEY_WOW64_32KEY | winreg.KEY_READ, versionStr) |
|
134 if exePath is not None: |
|
135 executables.add(exePath) |
|
136 |
|
137 # Even on Intel 64-bit machines it's 'AMD64' |
|
138 if platform.machine() == 'AMD64': |
|
139 exePath = getExePath( |
|
140 winreg.HKEY_CURRENT_USER, |
|
141 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
|
142 if exePath is not None: |
|
143 executables.add(exePath) |
|
144 |
|
145 exePath = getExePath( |
|
146 winreg.HKEY_LOCAL_MACHINE, |
|
147 winreg.KEY_WOW64_64KEY | winreg.KEY_READ, versionStr) |
|
148 if exePath is not None: |
|
149 executables.add(exePath) |
|
150 else: |
|
151 # |
|
152 # Linux, Unix ... |
|
153 pyinstallerScripts = ['pyinstaller', 'pyi-makespec'] |
|
154 ## scriptSuffixes = [""] |
|
155 ## for minorVersion in minorVersions: |
|
156 ## scriptSuffixes.append( |
|
157 ## "-python{0}.{1}".format(majorVersion, minorVersion)) |
|
158 # There could be multiple pyinstaller executables in the path |
|
159 # e.g. for different python variants |
|
160 path = Utilities.getEnvironmentEntry('PATH') |
|
161 # environment variable not defined |
|
162 if path is None: |
|
163 return [] |
|
164 |
|
165 # step 1: determine possible candidates |
|
166 exes = [] |
|
167 dirs = path.split(os.pathsep) |
|
168 for directory in dirs: |
|
169 for pyinstallerScript in pyinstallerScripts: |
|
170 exe = os.path.join(directory, pyinstallerScript) |
|
171 if os.access(exe, os.X_OK): |
|
172 exes.append(exe) |
|
173 |
|
174 # step 2: determine the Python variant |
|
175 if Utilities.isMacPlatform(): |
|
176 checkStrings = ["Python.framework/Versions/3".lower(), |
|
177 "python3"] |
|
178 else: |
|
179 checkStrings = ["python3"] |
|
180 |
|
181 _exePy2 = set() |
|
182 _exePy3 = set() |
|
183 for exe in exes: |
|
184 try: |
|
185 f = open(exe, "r") |
|
186 line0 = f.readline() |
|
187 for checkStr in checkStrings: |
|
188 if checkStr in line0.lower(): |
|
189 _exePy3.add(exe) |
|
190 break |
|
191 else: |
|
192 _exePy2.add(exe) |
|
193 finally: |
|
194 f.close() |
|
195 |
|
196 executables = _exePy3 if majorVersion == 3 else _exePy2 |
|
197 |
|
198 # sort items, the probably newest topmost |
|
199 executables = list(executables) |
|
200 executables.sort(reverse=True) |
|
201 return executables |
|
202 |
|
203 |
|
204 def _checkProgram(): |
|
205 """ |
|
206 Restricted function to check the availability of pyinstaller. |
|
207 |
|
208 @return flag indicating availability (boolean) |
|
209 """ |
|
210 global error, exePy2, exePy3 |
|
211 |
|
212 exePy2 = _findExecutable(2) |
|
213 exePy3 = _findExecutable(3) |
|
214 if (exePy2 + exePy3) == []: |
|
215 if Utilities.isWindowsPlatform(): |
|
216 error = QCoreApplication.translate( |
|
217 "PyInstallerPlugin", |
|
218 "The pyinstaller.exe executable could not be found." |
|
219 ) |
|
220 else: |
|
221 error = QCoreApplication.translate( |
|
222 "PyInstallerPlugin", |
|
223 "The pyinstaller executable could not be found." |
|
224 ) |
|
225 return False |
|
226 else: |
|
227 return True |
|
228 |
|
229 |
|
230 class PyInstallerPlugin(QObject): |
|
231 """ |
|
232 Class documentation goes here. |
|
233 """ |
|
234 def __init__(self, ui): |
|
235 """ |
|
236 Constructor |
|
237 |
|
238 @param ui reference to the user interface object (UI.UserInterface) |
|
239 """ |
|
240 super(PyInstallerPlugin, self).__init__(ui) |
|
241 self.__ui = ui |
|
242 |
|
243 def activate(self): |
|
244 """ |
|
245 Public method to activate this plugin. |
|
246 |
|
247 @return tuple of None and activation status (boolean) |
|
248 """ |
|
249 global error |
|
250 error = "" # clear previous error |
|
251 |
|
252 return None, True |
|
253 |
|
254 def deactivate(self): |
|
255 """ |
|
256 Public method to deactivate this plugin. |
|
257 """ |
|
258 pass |