scripts/install.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9218
71cf3979a6c9
child 9231
d92a18dffa5e
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
20 import os 20 import os
21 import py_compile 21 import py_compile
22 import re 22 import re
23 import shlex 23 import shlex
24 import shutil 24 import shutil
25 import subprocess # secok 25 import subprocess # secok
26 import time 26 import time
27 import sys 27 import sys
28 28
29 # Define the globals. 29 # Define the globals.
30 progName = None 30 progName = None
45 verbose = False 45 verbose = False
46 cfg = {} 46 cfg = {}
47 progLanguages = ["Python", "Ruby", "QSS"] 47 progLanguages = ["Python", "Ruby", "QSS"]
48 sourceDir = "eric" 48 sourceDir = "eric"
49 eric7SourceDir = "" 49 eric7SourceDir = ""
50 configName = 'eric7config.py' 50 configName = "eric7config.py"
51 defaultMacAppBundleName = "eric7.app" 51 defaultMacAppBundleName = "eric7.app"
52 defaultMacAppBundlePath = "/Applications" 52 defaultMacAppBundlePath = "/Applications"
53 defaultMacPythonExe = "{0}/Resources/Python.app/Contents/MacOS/Python".format( 53 defaultMacPythonExe = "{0}/Resources/Python.app/Contents/MacOS/Python".format(
54 sys.exec_prefix) 54 sys.exec_prefix
55 )
55 if not os.path.exists(defaultMacPythonExe): 56 if not os.path.exists(defaultMacPythonExe):
56 defaultMacPythonExe = "" 57 defaultMacPythonExe = ""
57 macAppBundleName = defaultMacAppBundleName 58 macAppBundleName = defaultMacAppBundleName
58 macAppBundlePath = defaultMacAppBundlePath 59 macAppBundlePath = defaultMacAppBundlePath
59 macPythonExe = defaultMacPythonExe 60 macPythonExe = defaultMacPythonExe
73 "windows": { 74 "windows": {
74 "sip": [], 75 "sip": [],
75 "PyQt6": [], 76 "PyQt6": [],
76 "QScintilla2": [], 77 "QScintilla2": [],
77 }, 78 },
78
79 "linux": { 79 "linux": {
80 "sip": [], 80 "sip": [],
81 "PyQt6": [], 81 "PyQt6": [],
82 "QScintilla2": [], 82 "QScintilla2": [],
83 }, 83 },
84
85 "mac": { 84 "mac": {
86 "sip": [], 85 "sip": [],
87 "PyQt6": [], 86 "PyQt6": [],
88 "QScintilla2": [], 87 "QScintilla2": [],
89 }, 88 },
91 90
92 91
93 def exit(rcode=0): 92 def exit(rcode=0):
94 """ 93 """
95 Exit the install script. 94 Exit the install script.
96 95
97 @param rcode result code to report back (integer) 96 @param rcode result code to report back (integer)
98 """ 97 """
99 global currDir 98 global currDir
100 99
101 print() 100 print()
102 101
103 if sys.platform.startswith(("win", "cygwin")): 102 if sys.platform.startswith(("win", "cygwin")):
104 with contextlib.suppress(): 103 with contextlib.suppress():
105 input("Press enter to continue...") # secok 104 input("Press enter to continue...") # secok
106 105
107 os.chdir(currDir) 106 os.chdir(currDir)
108 107
109 sys.exit(rcode) 108 sys.exit(rcode)
110 109
111 110
112 def usage(rcode=2): 111 def usage(rcode=2):
113 """ 112 """
119 global macAppBundleName, macAppBundlePath, macPythonExe 118 global macAppBundleName, macAppBundlePath, macPythonExe
120 119
121 print() 120 print()
122 print("Usage:") 121 print("Usage:")
123 if sys.platform == "darwin": 122 if sys.platform == "darwin":
124 print(" {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" 123 print(
125 " [-m name] [-n path] [-p python] [--help] [--no-apis]" 124 " {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]"
126 " [--no-info] [--no-tools] [--verbose] [--yes]" 125 " [-m name] [-n path] [-p python] [--help] [--no-apis]"
127 .format(progName)) 126 " [--no-info] [--no-tools] [--verbose] [--yes]".format(progName)
127 )
128 elif sys.platform.startswith(("win", "cygwin")): 128 elif sys.platform.startswith(("win", "cygwin")):
129 print(" {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file]" 129 print(
130 " [--clean-desktop] [--help] [--no-apis] [--no-info]" 130 " {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file]"
131 " [--no-tools] [--verbose] [--yes]" 131 " [--clean-desktop] [--help] [--no-apis] [--no-info]"
132 .format(progName)) 132 " [--no-tools] [--verbose] [--yes]".format(progName)
133 )
133 else: 134 else:
134 print(" {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]" 135 print(
135 " [--help] [--no-apis] [--no-info] [--no-tools] [--verbose]" 136 " {0} [-chvxz] [-a dir] [-b dir] [-d dir] [-f file] [-i dir]"
136 " [--yes]" 137 " [--help] [--no-apis] [--no-info] [--no-tools] [--verbose]"
137 .format(progName)) 138 " [--yes]".format(progName)
139 )
138 print("where:") 140 print("where:")
139 print(" -h, --help display this help message") 141 print(" -h, --help display this help message")
140 print(" -a dir where the API files will be installed") 142 print(" -a dir where the API files will be installed")
141 if apisDir: 143 if apisDir:
142 print(" (default: {0})".format(apisDir)) 144 print(" (default: {0})".format(apisDir))
145 print(" --no-apis don't install API files") 147 print(" --no-apis don't install API files")
146 print(" -b dir where the binaries will be installed") 148 print(" -b dir where the binaries will be installed")
147 print(" (default: {0})".format(platBinDir)) 149 print(" (default: {0})".format(platBinDir))
148 print(" -d dir where eric python files will be installed") 150 print(" -d dir where eric python files will be installed")
149 print(" (default: {0})".format(modDir)) 151 print(" (default: {0})".format(modDir))
150 print(" -f file configuration file naming the various installation" 152 print(" -f file configuration file naming the various installation" " paths")
151 " paths")
152 if not sys.platform.startswith(("win", "cygwin")): 153 if not sys.platform.startswith(("win", "cygwin")):
153 print(" -i dir temporary install prefix") 154 print(" -i dir temporary install prefix")
154 print(" (default: {0})".format(distDir)) 155 print(" (default: {0})".format(distDir))
155 if sys.platform == "darwin": 156 if sys.platform == "darwin":
156 print(" -m name name of the Mac app bundle") 157 print(" -m name name of the Mac app bundle")
164 if sys.platform.startswith(("win", "cygwin")): 165 if sys.platform.startswith(("win", "cygwin")):
165 print(" --clean-desktop delete desktop links before installation") 166 print(" --clean-desktop delete desktop links before installation")
166 print(" --no-info don't create the install info file") 167 print(" --no-info don't create the install info file")
167 print(" --no-tools don't install qt6-applications") 168 print(" --no-tools don't install qt6-applications")
168 print(" -v, --verbose print some more information") 169 print(" -v, --verbose print some more information")
169 print(" -x don't perform dependency checks (use on your own" 170 print(" -x don't perform dependency checks (use on your own" " risk)")
170 " risk)")
171 print(" -z don't compile the installed python files") 171 print(" -z don't compile the installed python files")
172 print(" --yes answer 'yes' to all questions") 172 print(" --yes answer 'yes' to all questions")
173 print() 173 print()
174 print("The file given to the -f option must be valid Python code" 174 print("The file given to the -f option must be valid Python code" " defining a")
175 " defining a") 175 print(
176 print("dictionary called 'cfg' with the keys 'ericDir', 'ericPixDir'," 176 "dictionary called 'cfg' with the keys 'ericDir', 'ericPixDir',"
177 " 'ericIconDir',") 177 " 'ericIconDir',"
178 )
178 print("'ericDTDDir', 'ericCSSDir', 'ericStylesDir', 'ericThemesDir',") 179 print("'ericDTDDir', 'ericCSSDir', 'ericStylesDir', 'ericThemesDir',")
179 print(" 'ericDocDir', ericExamplesDir',") 180 print(" 'ericDocDir', ericExamplesDir',")
180 print("'ericTranslationsDir', 'ericTemplatesDir', 'ericCodeTemplatesDir',") 181 print("'ericTranslationsDir', 'ericTemplatesDir', 'ericCodeTemplatesDir',")
181 print("'ericOthersDir','bindir', 'mdir' and 'apidir.") 182 print("'ericOthersDir','bindir', 'mdir' and 'apidir.")
182 print("These define the directories for the installation of the various" 183 print(
183 " parts of eric.") 184 "These define the directories for the installation of the various"
185 " parts of eric."
186 )
184 187
185 exit(rcode) 188 exit(rcode)
186 189
187 190
188 def initGlobals(): 191 def initGlobals():
189 """ 192 """
190 Module function to set the values of globals that need more than a 193 Module function to set the values of globals that need more than a
191 simple assignment. 194 simple assignment.
192 """ 195 """
193 global platBinDir, modDir, pyModDir, apisDir, platBinDirOld 196 global platBinDir, modDir, pyModDir, apisDir, platBinDirOld
194 197
195 import sysconfig 198 import sysconfig
196 199
197 if sys.platform.startswith(("win", "cygwin")): 200 if sys.platform.startswith(("win", "cygwin")):
198 platBinDir = sys.exec_prefix 201 platBinDir = sys.exec_prefix
199 if platBinDir.endswith("\\"): 202 if platBinDir.endswith("\\"):
200 platBinDir = platBinDir[:-1] 203 platBinDir = platBinDir[:-1]
201 platBinDirOld = platBinDir 204 platBinDirOld = platBinDir
208 # install the eric scripts along the python executable 211 # install the eric scripts along the python executable
209 platBinDir = pyBinDir 212 platBinDir = pyBinDir
210 else: 213 else:
211 # install them in the user's bin directory 214 # install them in the user's bin directory
212 platBinDir = os.path.expanduser("~/bin") 215 platBinDir = os.path.expanduser("~/bin")
213 if ( 216 if platBinDir != "/usr/local/bin" and os.access("/usr/local/bin", os.W_OK):
214 platBinDir != "/usr/local/bin" and
215 os.access("/usr/local/bin", os.W_OK)
216 ):
217 platBinDirOld = "/usr/local/bin" 217 platBinDirOld = "/usr/local/bin"
218 218
219 modDir = sysconfig.get_path('platlib') 219 modDir = sysconfig.get_path("platlib")
220 pyModDir = modDir 220 pyModDir = modDir
221 221
222 pyqtDataDir = os.path.join(modDir, "PyQt6") 222 pyqtDataDir = os.path.join(modDir, "PyQt6")
223 if os.path.exists(os.path.join(pyqtDataDir, "qsci")): 223 if os.path.exists(os.path.join(pyqtDataDir, "qsci")):
224 # it's the installer 224 # it's the installer
225 qtDataDir = pyqtDataDir 225 qtDataDir = pyqtDataDir
226 elif os.path.exists(os.path.join(pyqtDataDir, "Qt6", "qsci")): 226 elif os.path.exists(os.path.join(pyqtDataDir, "Qt6", "qsci")):
228 qtDataDir = os.path.join(pyqtDataDir, "Qt6") 228 qtDataDir = os.path.join(pyqtDataDir, "Qt6")
229 else: 229 else:
230 # determine dynamically 230 # determine dynamically
231 try: 231 try:
232 from PyQt6.QtCore import QLibraryInfo 232 from PyQt6.QtCore import QLibraryInfo
233
233 qtDataDir = QLibraryInfo.path(QLibraryInfo.LibraryPath.DataPath) 234 qtDataDir = QLibraryInfo.path(QLibraryInfo.LibraryPath.DataPath)
234 except ImportError: 235 except ImportError:
235 qtDataDir = None 236 qtDataDir = None
236 apisDir = os.path.join(qtDataDir, "qsci", "api") if qtDataDir else None 237 apisDir = os.path.join(qtDataDir, "qsci", "api") if qtDataDir else None
237 238
248 249
249 250
250 def copyDesktopFile(src, dst): 251 def copyDesktopFile(src, dst):
251 """ 252 """
252 Modify a desktop file and write it to its destination. 253 Modify a desktop file and write it to its destination.
253 254
254 @param src source file name (string) 255 @param src source file name (string)
255 @param dst destination file name (string) 256 @param dst destination file name (string)
256 """ 257 """
257 global cfg, platBinDir 258 global cfg, platBinDir
258 259
259 with open(src, "r", encoding="utf-8") as f: 260 with open(src, "r", encoding="utf-8") as f:
260 text = f.read() 261 text = f.read()
261 262
262 text = text.replace("@BINDIR@", platBinDir) 263 text = text.replace("@BINDIR@", platBinDir)
263 text = text.replace("@MARKER@", "") 264 text = text.replace("@MARKER@", "")
264 text = text.replace("@PY_MARKER@", "") 265 text = text.replace("@PY_MARKER@", "")
265 266
266 with open(dst, "w", encoding="utf-8") as f: 267 with open(dst, "w", encoding="utf-8") as f:
267 f.write(text) 268 f.write(text)
268 os.chmod(dst, 0o644) 269 os.chmod(dst, 0o644)
269 270
270 271
271 def copyAppStreamFile(src, dst): 272 def copyAppStreamFile(src, dst):
272 """ 273 """
273 Modify an appstream file and write it to its destination. 274 Modify an appstream file and write it to its destination.
274 275
275 @param src source file name (string) 276 @param src source file name (string)
276 @param dst destination file name (string) 277 @param dst destination file name (string)
277 """ 278 """
278 if os.path.exists(os.path.join("eric", "src", "eric7", "UI", "Info.py")): 279 if os.path.exists(os.path.join("eric", "src", "eric7", "UI", "Info.py")):
279 # Installing from installer archive 280 # Installing from installer archive
281 elif os.path.exists(os.path.join("src", "eric7", "UI", "Info.py")): 282 elif os.path.exists(os.path.join("src", "eric7", "UI", "Info.py")):
282 # Installing from source tree 283 # Installing from source tree
283 from src.eric7.UI.Info import Version 284 from src.eric7.UI.Info import Version
284 else: 285 else:
285 Version = "Unknown" 286 Version = "Unknown"
286 287
287 with open(src, "r", encoding="utf-8") as f: 288 with open(src, "r", encoding="utf-8") as f:
288 text = f.read() 289 text = f.read()
289 290
290 text = ( 291 text = (
291 text.replace("@MARKER@", "") 292 text.replace("@MARKER@", "")
292 .replace("@VERSION@", Version.split(None, 1)[0]) 293 .replace("@VERSION@", Version.split(None, 1)[0])
293 .replace("@DATE@", time.strftime("%Y-%m-%d")) 294 .replace("@DATE@", time.strftime("%Y-%m-%d"))
294 ) 295 )
295 296
296 with open(dst, "w", encoding="utf-8") as f: 297 with open(dst, "w", encoding="utf-8") as f:
297 f.write(text) 298 f.write(text)
298 os.chmod(dst, 0o644) 299 os.chmod(dst, 0o644)
299 300
300 301
301 def wrapperNames(dname, wfile): 302 def wrapperNames(dname, wfile):
302 """ 303 """
303 Create the platform specific names for the wrapper script. 304 Create the platform specific names for the wrapper script.
304 305
305 @param dname name of the directory to place the wrapper into 306 @param dname name of the directory to place the wrapper into
306 @param wfile basename (without extension) of the wrapper script 307 @param wfile basename (without extension) of the wrapper script
307 @return the names of the wrapper scripts 308 @return the names of the wrapper scripts
308 """ 309 """
309 wnames = ( 310 wnames = (
310 (dname + "\\" + wfile + ".cmd", dname + "\\" + wfile + ".bat") 311 (dname + "\\" + wfile + ".cmd", dname + "\\" + wfile + ".bat")
311 if sys.platform.startswith(("win", "cygwin")) else 312 if sys.platform.startswith(("win", "cygwin"))
312 (dname + "/" + wfile, ) 313 else (dname + "/" + wfile,)
313 ) 314 )
314 315
315 return wnames 316 return wnames
316 317
317 318
330 # all kinds of Windows systems 331 # all kinds of Windows systems
331 if sys.platform.startswith(("win", "cygwin")): 332 if sys.platform.startswith(("win", "cygwin")):
332 wname = wfile + ".cmd" 333 wname = wfile + ".cmd"
333 if isGuiScript: 334 if isGuiScript:
334 wrapper = ( 335 wrapper = (
335 '''@echo off\n''' 336 """@echo off\n"""
336 '''start "" "{2}\\pythonw.exe"''' 337 '''start "" "{2}\\pythonw.exe"'''
337 ''' "{0}\\{1}.pyw"''' 338 ''' "{0}\\{1}.pyw"'''
338 ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( 339 """ %1 %2 %3 %4 %5 %6 %7 %8 %9\n""".format(
339 pydir, wfile, os.path.dirname(sys.executable)) 340 pydir, wfile, os.path.dirname(sys.executable)
341 )
340 ) 342 )
341 else: 343 else:
342 wrapper = ( 344 wrapper = (
343 '''@"{0}" "{1}\\{2}.py"''' 345 '''@"{0}" "{1}\\{2}.py"'''
344 ''' %1 %2 %3 %4 %5 %6 %7 %8 %9\n'''.format( 346 """ %1 %2 %3 %4 %5 %6 %7 %8 %9\n""".format(sys.executable, pydir, wfile)
345 sys.executable, pydir, wfile)
346 ) 347 )
347 348
348 # Mac OS X 349 # Mac OS X
349 elif sys.platform == "darwin": 350 elif sys.platform == "darwin":
350 major = sys.version_info.major 351 major = sys.version_info.major
351 pyexec = "{0}/bin/pythonw{1}".format(sys.exec_prefix, major) 352 pyexec = "{0}/bin/pythonw{1}".format(sys.exec_prefix, major)
352 if not os.path.exists(pyexec): 353 if not os.path.exists(pyexec):
353 pyexec = "{0}/bin/python{1}".format(sys.exec_prefix, major) 354 pyexec = "{0}/bin/python{1}".format(sys.exec_prefix, major)
354 wname = wfile 355 wname = wfile
355 wrapper = ('''#!/bin/sh\n''' 356 wrapper = (
356 '''\n''' 357 """#!/bin/sh\n"""
357 '''exec "{0}" "{1}/{2}.py" "$@"\n''' 358 """\n"""
358 .format(pyexec, pydir, wfile)) 359 """exec "{0}" "{1}/{2}.py" "$@"\n""".format(pyexec, pydir, wfile)
360 )
359 361
360 # *nix systems 362 # *nix systems
361 else: 363 else:
362 wname = wfile 364 wname = wfile
363 wrapper = ('''#!/bin/sh\n''' 365 wrapper = (
364 '''\n''' 366 """#!/bin/sh\n"""
365 '''exec "{0}" "{1}/{2}.py" "$@"\n''' 367 """\n"""
366 .format(sys.executable, pydir, wfile)) 368 """exec "{0}" "{1}/{2}.py" "$@"\n""".format(sys.executable, pydir, wfile)
367 369 )
370
368 wname = os.path.join(saveDir, wname) 371 wname = os.path.join(saveDir, wname)
369 copyToFile(wname, wrapper) 372 copyToFile(wname, wrapper)
370 os.chmod(wname, 0o755) # secok 373 os.chmod(wname, 0o755) # secok
371 374
372 return wname 375 return wname
373 376
374 377
375 def copyTree(src, dst, filters, excludeDirs=None, excludePatterns=None): 378 def copyTree(src, dst, filters, excludeDirs=None, excludePatterns=None):
376 """ 379 """
377 Copy Python, translation, documentation, wizards configuration, 380 Copy Python, translation, documentation, wizards configuration,
378 designer template files and DTDs of a directory tree. 381 designer template files and DTDs of a directory tree.
379 382
380 @param src name of the source directory 383 @param src name of the source directory
381 @param dst name of the destination directory 384 @param dst name of the destination directory
382 @param filters list of filter pattern determining the files to be copied 385 @param filters list of filter pattern determining the files to be copied
383 @param excludeDirs list of (sub)directories to exclude from copying 386 @param excludeDirs list of (sub)directories to exclude from copying
384 @param excludePatterns list of filter pattern determining the files to 387 @param excludePatterns list of filter pattern determining the files to
391 try: 394 try:
392 names = os.listdir(src) 395 names = os.listdir(src)
393 except OSError: 396 except OSError:
394 # ignore missing directories (most probably the i18n directory) 397 # ignore missing directories (most probably the i18n directory)
395 return 398 return
396 399
397 for name in names: 400 for name in names:
398 skipIt = False 401 skipIt = False
399 for excludePattern in excludePatterns: 402 for excludePattern in excludePatterns:
400 if fnmatch.fnmatch(name, excludePattern): 403 if fnmatch.fnmatch(name, excludePattern):
401 skipIt = True 404 skipIt = True
410 shutil.copy2(srcname, dstname) 413 shutil.copy2(srcname, dstname)
411 os.chmod(dstname, 0o644) 414 os.chmod(dstname, 0o644)
412 break 415 break
413 else: 416 else:
414 if os.path.isdir(srcname) and srcname not in excludeDirs: 417 if os.path.isdir(srcname) and srcname not in excludeDirs:
415 copyTree(srcname, dstname, filters, 418 copyTree(srcname, dstname, filters, excludePatterns=excludePatterns)
416 excludePatterns=excludePatterns)
417 419
418 420
419 def createGlobalPluginsDir(): 421 def createGlobalPluginsDir():
420 """ 422 """
421 Create the global plugins directory, if it doesn't exist. 423 Create the global plugins directory, if it doesn't exist.
422 """ 424 """
423 global cfg, distDir 425 global cfg, distDir
424 426
425 pdir = os.path.join(cfg['mdir'], "eric7plugins") 427 pdir = os.path.join(cfg["mdir"], "eric7plugins")
426 fname = os.path.join(pdir, "__init__.py") 428 fname = os.path.join(pdir, "__init__.py")
427 if not os.path.exists(fname): 429 if not os.path.exists(fname):
428 if not os.path.exists(pdir): 430 if not os.path.exists(pdir):
429 os.mkdir(pdir, 0o755) 431 os.mkdir(pdir, 0o755)
430 with open(fname, "w") as f: 432 with open(fname, "w") as f:
441 443
442 def cleanupSource(dirName): 444 def cleanupSource(dirName):
443 """ 445 """
444 Cleanup the sources directory to get rid of leftover files 446 Cleanup the sources directory to get rid of leftover files
445 and directories. 447 and directories.
446 448
447 @param dirName name of the directory to prune (string) 449 @param dirName name of the directory to prune (string)
448 """ 450 """
449 # step 1: delete all Ui_*.py files without a corresponding 451 # step 1: delete all Ui_*.py files without a corresponding
450 # *.ui file 452 # *.ui file
451 dirListing = os.listdir(dirName) 453 dirListing = os.listdir(dirName)
452 for formName, sourceName in [ 454 for formName, sourceName in [
453 (f.replace('Ui_', "").replace(".py", ".ui"), f) 455 (f.replace("Ui_", "").replace(".py", ".ui"), f)
454 for f in dirListing if fnmatch.fnmatch(f, "Ui_*.py")]: 456 for f in dirListing
457 if fnmatch.fnmatch(f, "Ui_*.py")
458 ]:
455 if not os.path.exists(os.path.join(dirName, formName)): 459 if not os.path.exists(os.path.join(dirName, formName)):
456 os.remove(os.path.join(dirName, sourceName)) 460 os.remove(os.path.join(dirName, sourceName))
457 if os.path.exists(os.path.join(dirName, sourceName + "c")): 461 if os.path.exists(os.path.join(dirName, sourceName + "c")):
458 os.remove(os.path.join(dirName, sourceName + "c")) 462 os.remove(os.path.join(dirName, sourceName + "c"))
459 463
460 # step 2: delete the __pycache__ directory and all remaining *.pyc files 464 # step 2: delete the __pycache__ directory and all remaining *.pyc files
461 if os.path.exists(os.path.join(dirName, "__pycache__")): 465 if os.path.exists(os.path.join(dirName, "__pycache__")):
462 shutil.rmtree(os.path.join(dirName, "__pycache__")) 466 shutil.rmtree(os.path.join(dirName, "__pycache__"))
463 for name in [f for f in os.listdir(dirName) 467 for name in [f for f in os.listdir(dirName) if fnmatch.fnmatch(f, "*.pyc")]:
464 if fnmatch.fnmatch(f, "*.pyc")]:
465 os.remove(os.path.join(dirName, name)) 468 os.remove(os.path.join(dirName, name))
466 469
467 # step 3: delete *.orig files 470 # step 3: delete *.orig files
468 for name in [f for f in os.listdir(dirName) 471 for name in [f for f in os.listdir(dirName) if fnmatch.fnmatch(f, "*.orig")]:
469 if fnmatch.fnmatch(f, "*.orig")]:
470 os.remove(os.path.join(dirName, name)) 472 os.remove(os.path.join(dirName, name))
471 473
472 # step 4: descent into subdirectories and delete them if empty 474 # step 4: descent into subdirectories and delete them if empty
473 for name in os.listdir(dirName): 475 for name in os.listdir(dirName):
474 name = os.path.join(dirName, name) 476 name = os.path.join(dirName, name)
475 if os.path.isdir(name): 477 if os.path.isdir(name):
476 cleanupSource(name) 478 cleanupSource(name)
481 def cleanUp(): 483 def cleanUp():
482 """ 484 """
483 Uninstall the old eric files. 485 Uninstall the old eric files.
484 """ 486 """
485 global platBinDir, platBinDirOld 487 global platBinDir, platBinDirOld
486 488
487 try: 489 try:
488 from eric7config import getConfig 490 from eric7config import getConfig
489 except ImportError: 491 except ImportError:
490 # eric wasn't installed previously 492 # eric wasn't installed previously
491 return 493 return
492 except SyntaxError: 494 except SyntaxError:
493 # an incomplete or old config file was found 495 # an incomplete or old config file was found
494 return 496 return
495 497
496 global pyModDir, progLanguages 498 global pyModDir, progLanguages
497 499
498 # Remove the menu entry for Linux systems 500 # Remove the menu entry for Linux systems
499 if sys.platform.startswith("linux"): 501 if sys.platform.startswith("linux"):
500 cleanUpLinuxSpecifics() 502 cleanUpLinuxSpecifics()
501 # Remove the Desktop and Start Menu entries for Windows systems 503 # Remove the Desktop and Start Menu entries for Windows systems
502 elif sys.platform.startswith(("win", "cygwin")): 504 elif sys.platform.startswith(("win", "cygwin")):
503 cleanUpWindowsLinks() 505 cleanUpWindowsLinks()
504 506
505 # Remove the wrapper scripts 507 # Remove the wrapper scripts
506 rem_wnames = [ 508 rem_wnames = [
507 "eric7_api", "eric7_browser", "eric7_compare", "eric7_configure", 509 "eric7_api",
508 "eric7_diff", "eric7_doc", "eric7_editor", "eric7_hexeditor", 510 "eric7_browser",
509 "eric7_iconeditor", "eric7_plugininstall", "eric7_pluginrepository", 511 "eric7_compare",
510 "eric7_pluginuninstall", "eric7_qregularexpression", "eric7_re", 512 "eric7_configure",
511 "eric7_shell", "eric7_snap", "eric7_sqlbrowser", "eric7_testing", 513 "eric7_diff",
512 "eric7_tray", "eric7_trpreviewer", "eric7_uipreviewer", 514 "eric7_doc",
513 "eric7_virtualenv", "eric7", 515 "eric7_editor",
516 "eric7_hexeditor",
517 "eric7_iconeditor",
518 "eric7_plugininstall",
519 "eric7_pluginrepository",
520 "eric7_pluginuninstall",
521 "eric7_qregularexpression",
522 "eric7_re",
523 "eric7_shell",
524 "eric7_snap",
525 "eric7_sqlbrowser",
526 "eric7_testing",
527 "eric7_tray",
528 "eric7_trpreviewer",
529 "eric7_uipreviewer",
530 "eric7_virtualenv",
531 "eric7",
514 # obsolete scripts below 532 # obsolete scripts below
515 "eric7_unittest", 533 "eric7_unittest",
516 ] 534 ]
517 535
518 try: 536 try:
519 dirs = [platBinDir, getConfig('bindir')] 537 dirs = [platBinDir, getConfig("bindir")]
520 if platBinDirOld: 538 if platBinDirOld:
521 dirs.append(platBinDirOld) 539 dirs.append(platBinDirOld)
522 for rem_wname in rem_wnames: 540 for rem_wname in rem_wnames:
523 for d in dirs: 541 for d in dirs:
524 for rwname in wrapperNames(d, rem_wname): 542 for rwname in wrapperNames(d, rem_wname):
525 if os.path.exists(rwname): 543 if os.path.exists(rwname):
526 os.remove(rwname) 544 os.remove(rwname)
527 545
528 # Cleanup our config file(s) 546 # Cleanup our config file(s)
529 for name in ['eric7config.py', 'eric7config.pyc', 'eric7.pth']: 547 for name in ["eric7config.py", "eric7config.pyc", "eric7.pth"]:
530 e6cfile = os.path.join(pyModDir, name) 548 e6cfile = os.path.join(pyModDir, name)
531 if os.path.exists(e6cfile): 549 if os.path.exists(e6cfile):
532 os.remove(e6cfile) 550 os.remove(e6cfile)
533 e6cfile = os.path.join(pyModDir, "__pycache__", name) 551 e6cfile = os.path.join(pyModDir, "__pycache__", name)
534 path, ext = os.path.splitext(e6cfile) 552 path, ext = os.path.splitext(e6cfile)
535 for f in glob.glob("{0}.*{1}".format(path, ext)): 553 for f in glob.glob("{0}.*{1}".format(path, ext)):
536 os.remove(f) 554 os.remove(f)
537 555
538 # Cleanup the install directories 556 # Cleanup the install directories
539 for name in ['ericExamplesDir', 'ericDocDir', 'ericDTDDir', 557 for name in [
540 'ericCSSDir', 'ericIconDir', 'ericPixDir', 558 "ericExamplesDir",
541 'ericTemplatesDir', 'ericCodeTemplatesDir', 559 "ericDocDir",
542 'ericOthersDir', 'ericStylesDir', 'ericThemesDir', 560 "ericDTDDir",
543 'ericDir']: 561 "ericCSSDir",
562 "ericIconDir",
563 "ericPixDir",
564 "ericTemplatesDir",
565 "ericCodeTemplatesDir",
566 "ericOthersDir",
567 "ericStylesDir",
568 "ericThemesDir",
569 "ericDir",
570 ]:
544 with contextlib.suppress(AttributeError): 571 with contextlib.suppress(AttributeError):
545 if os.path.exists(getConfig(name)): 572 if os.path.exists(getConfig(name)):
546 shutil.rmtree(getConfig(name), True) 573 shutil.rmtree(getConfig(name), True)
547 574
548 # Cleanup translations 575 # Cleanup translations
549 for name in glob.glob( 576 for name in glob.glob(
550 os.path.join(getConfig('ericTranslationsDir'), 'eric7_*.qm')): 577 os.path.join(getConfig("ericTranslationsDir"), "eric7_*.qm")
578 ):
551 if os.path.exists(name): 579 if os.path.exists(name):
552 os.remove(name) 580 os.remove(name)
553 581
554 # Cleanup API files 582 # Cleanup API files
555 with contextlib.suppress(AttributeError): 583 with contextlib.suppress(AttributeError):
556 apidir = getConfig('apidir') 584 apidir = getConfig("apidir")
557 for progLanguage in progLanguages: 585 for progLanguage in progLanguages:
558 for name in getConfig('apis'): 586 for name in getConfig("apis"):
559 apiname = os.path.join(apidir, progLanguage.lower(), name) 587 apiname = os.path.join(apidir, progLanguage.lower(), name)
560 if os.path.exists(apiname): 588 if os.path.exists(apiname):
561 os.remove(apiname) 589 os.remove(apiname)
562 for apiname in glob.glob( 590 for apiname in glob.glob(
563 os.path.join(apidir, progLanguage.lower(), "*.bas")): 591 os.path.join(apidir, progLanguage.lower(), "*.bas")
592 ):
564 if os.path.basename(apiname) != "eric7.bas": 593 if os.path.basename(apiname) != "eric7.bas":
565 os.remove(apiname) 594 os.remove(apiname)
566 595
567 if sys.platform == "darwin": 596 if sys.platform == "darwin":
568 # delete the Mac app bundle 597 # delete the Mac app bundle
569 cleanUpMacAppBundle() 598 cleanUpMacAppBundle()
570 except OSError as msg: 599 except OSError as msg:
571 sys.stderr.write( 600 sys.stderr.write("Error: {0}\nTry install with admin rights.\n".format(msg))
572 'Error: {0}\nTry install with admin rights.\n'.format(msg))
573 exit(7) 601 exit(7)
574 602
575 603
576 def cleanUpLinuxSpecifics(): 604 def cleanUpLinuxSpecifics():
577 """ 605 """
582 "/usr/share/applications/eric7.desktop", 610 "/usr/share/applications/eric7.desktop",
583 "/usr/share/appdata/eric7.appdata.xml", 611 "/usr/share/appdata/eric7.appdata.xml",
584 "/usr/share/metainfo/eric7.appdata.xml", 612 "/usr/share/metainfo/eric7.appdata.xml",
585 "/usr/share/pixmaps/eric.png", 613 "/usr/share/pixmaps/eric.png",
586 "/usr/share/icons/eric.png", 614 "/usr/share/icons/eric.png",
587
588 "/usr/share/applications/eric7_browser.desktop", 615 "/usr/share/applications/eric7_browser.desktop",
589 "/usr/share/pixmaps/ericWeb.png", 616 "/usr/share/pixmaps/ericWeb.png",
590 "/usr/share/icons/ericWeb.png", 617 "/usr/share/icons/ericWeb.png",
591 ]: 618 ]:
592 if os.path.exists(name): 619 if os.path.exists(name):
597 "~/.local/share/applications/eric7.desktop", 624 "~/.local/share/applications/eric7.desktop",
598 "~/.local/share/appdata/eric7.appdata.xml", 625 "~/.local/share/appdata/eric7.appdata.xml",
599 "~/.local/share/metainfo/eric7.appdata.xml", 626 "~/.local/share/metainfo/eric7.appdata.xml",
600 "~/.local/share/pixmaps/eric.png", 627 "~/.local/share/pixmaps/eric.png",
601 "~/.local/share/icons/eric.png", 628 "~/.local/share/icons/eric.png",
602
603 "~/.local/share/applications/eric7_browser.desktop", 629 "~/.local/share/applications/eric7_browser.desktop",
604 "~/.local/share/pixmaps/ericWeb.png", 630 "~/.local/share/pixmaps/ericWeb.png",
605 "~/.local/share/icons/ericWeb.png", 631 "~/.local/share/icons/ericWeb.png",
606 ]: 632 ]:
607 path = os.path.expanduser(name) 633 path = os.path.expanduser(name)
612 def cleanUpMacAppBundle(): 638 def cleanUpMacAppBundle():
613 """ 639 """
614 Uninstall the macOS application bundle. 640 Uninstall the macOS application bundle.
615 """ 641 """
616 from eric7config import getConfig 642 from eric7config import getConfig
643
617 try: 644 try:
618 macAppBundlePath = getConfig("macAppBundlePath") 645 macAppBundlePath = getConfig("macAppBundlePath")
619 macAppBundleName = getConfig("macAppBundleName") 646 macAppBundleName = getConfig("macAppBundleName")
620 except AttributeError: 647 except AttributeError:
621 macAppBundlePath = defaultMacAppBundlePath 648 macAppBundlePath = defaultMacAppBundlePath
622 macAppBundleName = defaultMacAppBundleName 649 macAppBundleName = defaultMacAppBundleName
623 for bundlePath in [os.path.join(defaultMacAppBundlePath, 650 for bundlePath in [
624 macAppBundleName), 651 os.path.join(defaultMacAppBundlePath, macAppBundleName),
625 os.path.join(macAppBundlePath, 652 os.path.join(macAppBundlePath, macAppBundleName),
626 macAppBundleName), 653 ]:
627 ]:
628 if os.path.exists(bundlePath): 654 if os.path.exists(bundlePath):
629 shutil.rmtree(bundlePath) 655 shutil.rmtree(bundlePath)
630 656
631 657
632 def cleanUpWindowsLinks(): 658 def cleanUpWindowsLinks():
633 """ 659 """
634 Clean up the Desktop and Start Menu entries for Windows. 660 Clean up the Desktop and Start Menu entries for Windows.
635 """ 661 """
636 global doCleanDesktopLinks, forceCleanDesktopLinks 662 global doCleanDesktopLinks, forceCleanDesktopLinks
637 663
638 try: 664 try:
639 from pywintypes import com_error # __IGNORE_WARNING__ 665 from pywintypes import com_error # __IGNORE_WARNING__
640 except ImportError: 666 except ImportError:
641 # links were not created by install.py 667 # links were not created by install.py
642 return 668 return
643 669
644 regPath = ( 670 regPath = (
645 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + 671 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"
646 "\\User Shell Folders" 672 + "\\User Shell Folders"
647 ) 673 )
648 674
649 if doCleanDesktopLinks or forceCleanDesktopLinks: 675 if doCleanDesktopLinks or forceCleanDesktopLinks:
650 # 1. cleanup desktop links 676 # 1. cleanup desktop links
651 regName = "Desktop" 677 regName = "Desktop"
652 desktopEntry = getWinregEntry(regName, regPath) 678 desktopEntry = getWinregEntry(regName, regPath)
653 if desktopEntry: 679 if desktopEntry:
658 try: 684 try:
659 os.remove(linkPath) 685 os.remove(linkPath)
660 except OSError: 686 except OSError:
661 # maybe restrictions prohibited link removal 687 # maybe restrictions prohibited link removal
662 print("Could not remove '{0}'.".format(linkPath)) 688 print("Could not remove '{0}'.".format(linkPath))
663 689
664 # 2. cleanup start menu entry 690 # 2. cleanup start menu entry
665 regName = "Programs" 691 regName = "Programs"
666 programsEntry = getWinregEntry(regName, regPath) 692 programsEntry = getWinregEntry(regName, regPath)
667 if programsEntry: 693 if programsEntry:
668 programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) 694 programsFolder = os.path.normpath(os.path.expandvars(programsEntry))
676 702
677 703
678 def shutilCopy(src, dst, perm=0o644): 704 def shutilCopy(src, dst, perm=0o644):
679 """ 705 """
680 Wrapper function around shutil.copy() to ensure the permissions. 706 Wrapper function around shutil.copy() to ensure the permissions.
681 707
682 @param src source file name (string) 708 @param src source file name (string)
683 @param dst destination file name or directory name (string) 709 @param dst destination file name or directory name (string)
684 @param perm permissions to be set (integer) 710 @param perm permissions to be set (integer)
685 """ 711 """
686 shutil.copy(src, dst) 712 shutil.copy(src, dst)
690 716
691 717
692 def installEric(): 718 def installEric():
693 """ 719 """
694 Actually perform the installation steps. 720 Actually perform the installation steps.
695 721
696 @return result code (integer) 722 @return result code (integer)
697 """ 723 """
698 global distDir, doCleanup, cfg, progLanguages, sourceDir, configName 724 global distDir, doCleanup, cfg, progLanguages, sourceDir, configName
699 global installApis 725 global installApis
700 726
701 # Create the platform specific wrappers. 727 # Create the platform specific wrappers.
702 scriptsDir = "install_scripts" 728 scriptsDir = "install_scripts"
703 if not os.path.isdir(scriptsDir): 729 if not os.path.isdir(scriptsDir):
704 os.mkdir(scriptsDir) 730 os.mkdir(scriptsDir)
705 wnames = [] 731 wnames = []
706 for name in ["eric7_api", "eric7_doc"]: 732 for name in ["eric7_api", "eric7_doc"]:
707 wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir, False)) 733 wnames.append(createPyWrapper(cfg["ericDir"], name, scriptsDir, False))
708 for name in ["eric7_browser", "eric7_compare", "eric7_configure", 734 for name in [
709 "eric7_diff", "eric7_editor", "eric7_hexeditor", 735 "eric7_browser",
710 "eric7_iconeditor", "eric7_plugininstall", 736 "eric7_compare",
711 "eric7_pluginrepository", "eric7_pluginuninstall", 737 "eric7_configure",
712 "eric7_qregularexpression", "eric7_re", "eric7_shell", 738 "eric7_diff",
713 "eric7_snap", "eric7_sqlbrowser", "eric7_tray", 739 "eric7_editor",
714 "eric7_trpreviewer", "eric7_uipreviewer", "eric7_testing", 740 "eric7_hexeditor",
715 "eric7_virtualenv", "eric7"]: 741 "eric7_iconeditor",
716 wnames.append(createPyWrapper(cfg['ericDir'], name, scriptsDir)) 742 "eric7_plugininstall",
717 743 "eric7_pluginrepository",
744 "eric7_pluginuninstall",
745 "eric7_qregularexpression",
746 "eric7_re",
747 "eric7_shell",
748 "eric7_snap",
749 "eric7_sqlbrowser",
750 "eric7_tray",
751 "eric7_trpreviewer",
752 "eric7_uipreviewer",
753 "eric7_testing",
754 "eric7_virtualenv",
755 "eric7",
756 ]:
757 wnames.append(createPyWrapper(cfg["ericDir"], name, scriptsDir))
758
718 # set install prefix, if not None 759 # set install prefix, if not None
719 if distDir: 760 if distDir:
720 for key in list(cfg.keys()): 761 for key in list(cfg.keys()):
721 cfg[key] = os.path.normpath( 762 cfg[key] = os.path.normpath(os.path.join(distDir, cfg[key].lstrip(os.sep)))
722 os.path.join(distDir, cfg[key].lstrip(os.sep))) 763
723
724 try: 764 try:
725 # Install the files 765 # Install the files
726 # make the install directories 766 # make the install directories
727 for key in cfg: 767 for key in cfg:
728 if cfg[key] and not os.path.isdir(cfg[key]): 768 if cfg[key] and not os.path.isdir(cfg[key]):
729 os.makedirs(cfg[key]) 769 os.makedirs(cfg[key])
730 770
731 # copy the eric config file 771 # copy the eric config file
732 if distDir: 772 if distDir:
733 shutilCopy(configName, cfg['mdir']) 773 shutilCopy(configName, cfg["mdir"])
734 if os.path.exists(configName + 'c'): 774 if os.path.exists(configName + "c"):
735 shutilCopy(configName + 'c', cfg['mdir']) 775 shutilCopy(configName + "c", cfg["mdir"])
736 else: 776 else:
737 shutilCopy(configName, modDir) 777 shutilCopy(configName, modDir)
738 if os.path.exists(configName + 'c'): 778 if os.path.exists(configName + "c"):
739 shutilCopy(configName + 'c', modDir) 779 shutilCopy(configName + "c", modDir)
740 780
741 # copy the various parts of eric 781 # copy the various parts of eric
742 copyTree( 782 copyTree(
743 eric7SourceDir, 783 eric7SourceDir,
744 cfg['ericDir'], 784 cfg["ericDir"],
745 ['*.py', '*.pyc', '*.pyo', '*.pyw'], 785 ["*.py", "*.pyc", "*.pyo", "*.pyw"],
746 excludePatterns=["eric7config.py*"]) 786 excludePatterns=["eric7config.py*"],
787 )
747 copyTree( 788 copyTree(
748 os.path.join(eric7SourceDir, "Plugins"), 789 os.path.join(eric7SourceDir, "Plugins"),
749 os.path.join(cfg['ericDir'], "Plugins"), 790 os.path.join(cfg["ericDir"], "Plugins"),
750 ['*.svgz', '*.svg', '*.png', '*.style', '*.tmpl', '*.txt']) 791 ["*.svgz", "*.svg", "*.png", "*.style", "*.tmpl", "*.txt"],
792 )
751 copyTree( 793 copyTree(
752 os.path.join(eric7SourceDir, "Documentation"), 794 os.path.join(eric7SourceDir, "Documentation"),
753 cfg['ericDocDir'], 795 cfg["ericDocDir"],
754 ['*.html', '*.qch']) 796 ["*.html", "*.qch"],
755 copyTree( 797 )
756 os.path.join(eric7SourceDir, "CSSs"), 798 copyTree(os.path.join(eric7SourceDir, "CSSs"), cfg["ericCSSDir"], ["*.css"])
757 cfg['ericCSSDir'],
758 ['*.css'])
759 copyTree( 799 copyTree(
760 os.path.join(eric7SourceDir, "Styles"), 800 os.path.join(eric7SourceDir, "Styles"),
761 cfg['ericStylesDir'], 801 cfg["ericStylesDir"],
762 ['*.qss', '*.ehj']) 802 ["*.qss", "*.ehj"],
803 )
763 copyTree( 804 copyTree(
764 os.path.join(eric7SourceDir, "Themes"), 805 os.path.join(eric7SourceDir, "Themes"), cfg["ericThemesDir"], ["*.ethj"]
765 cfg['ericThemesDir'], 806 )
766 ['*.ethj'])
767 copyTree( 807 copyTree(
768 os.path.join(eric7SourceDir, "i18n"), 808 os.path.join(eric7SourceDir, "i18n"), cfg["ericTranslationsDir"], ["*.qm"]
769 cfg['ericTranslationsDir'], 809 )
770 ['*.qm'])
771 copyTree( 810 copyTree(
772 os.path.join(eric7SourceDir, "icons"), 811 os.path.join(eric7SourceDir, "icons"),
773 cfg['ericIconDir'], 812 cfg["ericIconDir"],
774 ['*.svgz', '*.svg', '*.png', 'LICENSE*.*', 'readme.txt']) 813 ["*.svgz", "*.svg", "*.png", "LICENSE*.*", "readme.txt"],
814 )
775 copyTree( 815 copyTree(
776 os.path.join(eric7SourceDir, "pixmaps"), 816 os.path.join(eric7SourceDir, "pixmaps"),
777 cfg['ericPixDir'], 817 cfg["ericPixDir"],
778 ['*.svgz', '*.svg', '*.png', '*.xpm', '*.ico', '*.gif']) 818 ["*.svgz", "*.svg", "*.png", "*.xpm", "*.ico", "*.gif"],
819 )
779 copyTree( 820 copyTree(
780 os.path.join(eric7SourceDir, "DesignerTemplates"), 821 os.path.join(eric7SourceDir, "DesignerTemplates"),
781 cfg['ericTemplatesDir'], 822 cfg["ericTemplatesDir"],
782 ['*.tmpl']) 823 ["*.tmpl"],
824 )
783 copyTree( 825 copyTree(
784 os.path.join(eric7SourceDir, "CodeTemplates"), 826 os.path.join(eric7SourceDir, "CodeTemplates"),
785 cfg['ericCodeTemplatesDir'], 827 cfg["ericCodeTemplatesDir"],
786 ['*.tmpl']) 828 ["*.tmpl"],
829 )
787 copyTree( 830 copyTree(
788 os.path.join(eric7SourceDir, "DebugClients", "Python", "coverage"), 831 os.path.join(eric7SourceDir, "DebugClients", "Python", "coverage"),
789 os.path.join(cfg['ericDir'], "DebugClients", "Python", "coverage"), 832 os.path.join(cfg["ericDir"], "DebugClients", "Python", "coverage"),
790 ['*.js', '*.html', '*.png', '*.css', '*.scss', '*.txt', '*.rst']) 833 ["*.js", "*.html", "*.png", "*.css", "*.scss", "*.txt", "*.rst"],
791 834 )
835
792 # copy some data files needed at various places 836 # copy some data files needed at various places
793 copyTree( 837 copyTree(
794 os.path.join(eric7SourceDir, "data"), 838 os.path.join(eric7SourceDir, "data"),
795 os.path.join(cfg['ericDir'], "data"), 839 os.path.join(cfg["ericDir"], "data"),
796 ['*.txt']) 840 ["*.txt"],
841 )
797 copyTree( 842 copyTree(
798 os.path.join(eric7SourceDir, "EricNetwork", "data"), 843 os.path.join(eric7SourceDir, "EricNetwork", "data"),
799 os.path.join(cfg['ericDir'], "EricNetwork", "data"), 844 os.path.join(cfg["ericDir"], "EricNetwork", "data"),
800 ['*.dat', '*.txt']) 845 ["*.dat", "*.txt"],
846 )
801 copyTree( 847 copyTree(
802 os.path.join(eric7SourceDir, "IconEditor", "cursors"), 848 os.path.join(eric7SourceDir, "IconEditor", "cursors"),
803 os.path.join(cfg['ericDir'], "IconEditor", "cursors"), 849 os.path.join(cfg["ericDir"], "IconEditor", "cursors"),
804 ['*.xpm']) 850 ["*.xpm"],
851 )
805 copyTree( 852 copyTree(
806 os.path.join(eric7SourceDir, "UI", "data"), 853 os.path.join(eric7SourceDir, "UI", "data"),
807 os.path.join(cfg['ericDir'], "UI", "data"), 854 os.path.join(cfg["ericDir"], "UI", "data"),
808 ['*.css']) 855 ["*.css"],
856 )
809 copyTree( 857 copyTree(
810 os.path.join(eric7SourceDir, "WebBrowser"), 858 os.path.join(eric7SourceDir, "WebBrowser"),
811 os.path.join(cfg['ericDir'], "WebBrowser"), 859 os.path.join(cfg["ericDir"], "WebBrowser"),
812 ['*.xbel', '*.xml', '*.html', '*.png', '*.gif', '*.js']) 860 ["*.xbel", "*.xml", "*.html", "*.png", "*.gif", "*.js"],
813 861 )
862
814 # copy the wrappers 863 # copy the wrappers
815 for wname in wnames: 864 for wname in wnames:
816 shutilCopy(wname, cfg['bindir'], perm=0o755) 865 shutilCopy(wname, cfg["bindir"], perm=0o755)
817 os.remove(wname) 866 os.remove(wname)
818 shutil.rmtree(scriptsDir) 867 shutil.rmtree(scriptsDir)
819 868
820 # copy the license file 869 # copy the license file
821 shutilCopy(os.path.join(sourceDir, "docs", "LICENSE.GPL3"), 870 shutilCopy(os.path.join(sourceDir, "docs", "LICENSE.GPL3"), cfg["ericDir"])
822 cfg['ericDir']) 871
823
824 # create the global plugins directory 872 # create the global plugins directory
825 createGlobalPluginsDir() 873 createGlobalPluginsDir()
826 874
827 except OSError as msg: 875 except OSError as msg:
828 sys.stderr.write( 876 sys.stderr.write("Error: {0}\nTry install with admin rights.\n".format(msg))
829 'Error: {0}\nTry install with admin rights.\n'.format(msg)) 877 return 7
830 return(7) 878
831
832 # copy some text files to the doc area 879 # copy some text files to the doc area
833 for name in ["LICENSE.GPL3", "THANKS", "changelog"]: 880 for name in ["LICENSE.GPL3", "THANKS", "changelog"]:
834 try: 881 try:
835 shutilCopy(os.path.join(sourceDir, "docs", name), 882 shutilCopy(os.path.join(sourceDir, "docs", name), cfg["ericDocDir"])
836 cfg['ericDocDir'])
837 except OSError: 883 except OSError:
838 print("Could not install '{0}'.".format( 884 print(
839 os.path.join(sourceDir, "docs", name))) 885 "Could not install '{0}'.".format(os.path.join(sourceDir, "docs", name))
840 for name in glob.glob(os.path.join(sourceDir, 'docs', 'README*.*')): 886 )
887 for name in glob.glob(os.path.join(sourceDir, "docs", "README*.*")):
841 try: 888 try:
842 shutilCopy(name, cfg['ericDocDir']) 889 shutilCopy(name, cfg["ericDocDir"])
843 except OSError: 890 except OSError:
844 print("Could not install '{0}'.".format(name)) 891 print("Could not install '{0}'.".format(name))
845 892
846 # copy some more stuff 893 # copy some more stuff
847 for name in ('default.ekj', 'default_Mac.ekj', 894 for name in ("default.ekj", "default_Mac.ekj", "default.e4k", "default_Mac.e4k"):
848 'default.e4k', 'default_Mac.e4k'):
849 try: 895 try:
850 shutilCopy(os.path.join(sourceDir, "others", name), 896 shutilCopy(os.path.join(sourceDir, "others", name), cfg["ericOthersDir"])
851 cfg['ericOthersDir'])
852 except OSError: 897 except OSError:
853 print("Could not install '{0}'.".format( 898 print(
854 os.path.join(sourceDir, "others", name))) 899 "Could not install '{0}'.".format(
855 900 os.path.join(sourceDir, "others", name)
901 )
902 )
903
856 # install the API file 904 # install the API file
857 if installApis: 905 if installApis:
858 for progLanguage in progLanguages: 906 for progLanguage in progLanguages:
859 apidir = os.path.join(cfg['apidir'], progLanguage.lower()) 907 apidir = os.path.join(cfg["apidir"], progLanguage.lower())
860 print("Installing {0} API files to '{1}'.".format( 908 print("Installing {0} API files to '{1}'.".format(progLanguage, apidir))
861 progLanguage, apidir))
862 if not os.path.exists(apidir): 909 if not os.path.exists(apidir):
863 os.makedirs(apidir) 910 os.makedirs(apidir)
864 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 911 for apiName in glob.glob(
865 progLanguage, "*.api")): 912 os.path.join(eric7SourceDir, "APIs", progLanguage, "*.api")
913 ):
866 try: 914 try:
867 shutilCopy(apiName, apidir) 915 shutilCopy(apiName, apidir)
868 except OSError: 916 except OSError:
869 print("Could not install '{0}' (no permission)." 917 print("Could not install '{0}' (no permission).".format(apiName))
870 .format(apiName)) 918 for apiName in glob.glob(
871 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 919 os.path.join(eric7SourceDir, "APIs", progLanguage, "*.bas")
872 progLanguage, "*.bas")): 920 ):
873 try: 921 try:
874 shutilCopy(apiName, apidir) 922 shutilCopy(apiName, apidir)
875 except OSError: 923 except OSError:
876 print("Could not install '{0}' (no permission)." 924 print("Could not install '{0}' (no permission).".format(apiName))
877 .format(apiName))
878 if progLanguage == "Python": 925 if progLanguage == "Python":
879 # copy Python3 API files to the same destination 926 # copy Python3 API files to the same destination
880 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 927 for apiName in glob.glob(
881 "Python3", "*.api")): 928 os.path.join(eric7SourceDir, "APIs", "Python3", "*.api")
929 ):
882 try: 930 try:
883 shutilCopy(apiName, apidir) 931 shutilCopy(apiName, apidir)
884 except OSError: 932 except OSError:
885 print("Could not install '{0}' (no permission)." 933 print(
886 .format(apiName)) 934 "Could not install '{0}' (no permission).".format(apiName)
887 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 935 )
888 "Python3", "*.bas")): 936 for apiName in glob.glob(
889 if os.path.exists(os.path.join( 937 os.path.join(eric7SourceDir, "APIs", "Python3", "*.bas")
890 apidir, os.path.basename( 938 ):
891 apiName.replace(".bas", ".api")))): 939 if os.path.exists(
940 os.path.join(
941 apidir, os.path.basename(apiName.replace(".bas", ".api"))
942 )
943 ):
892 try: 944 try:
893 shutilCopy(apiName, apidir) 945 shutilCopy(apiName, apidir)
894 except OSError: 946 except OSError:
895 print("Could not install '{0}' (no permission)." 947 print(
896 .format(apiName)) 948 "Could not install '{0}' (no permission).".format(
897 949 apiName
950 )
951 )
952
898 # copy MicroPython API files to the same destination 953 # copy MicroPython API files to the same destination
899 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 954 for apiName in glob.glob(
900 "MicroPython", "*.api")): 955 os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.api")
956 ):
901 try: 957 try:
902 shutilCopy(apiName, apidir) 958 shutilCopy(apiName, apidir)
903 except OSError: 959 except OSError:
904 print("Could not install '{0}' (no permission)." 960 print(
905 .format(apiName)) 961 "Could not install '{0}' (no permission).".format(apiName)
906 for apiName in glob.glob(os.path.join(eric7SourceDir, "APIs", 962 )
907 "MicroPython", "*.bas")): 963 for apiName in glob.glob(
908 if os.path.exists(os.path.join( 964 os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.bas")
909 apidir, os.path.basename( 965 ):
910 apiName.replace(".bas", ".api")))): 966 if os.path.exists(
967 os.path.join(
968 apidir, os.path.basename(apiName.replace(".bas", ".api"))
969 )
970 ):
911 try: 971 try:
912 shutilCopy(apiName, apidir) 972 shutilCopy(apiName, apidir)
913 except OSError: 973 except OSError:
914 print("Could not install '{0}' (no permission)." 974 print(
915 .format(apiName)) 975 "Could not install '{0}' (no permission).".format(
916 976 apiName
977 )
978 )
979
917 # Create menu entry for Linux systems 980 # Create menu entry for Linux systems
918 if sys.platform.startswith("linux"): 981 if sys.platform.startswith("linux"):
919 createLinuxSpecifics() 982 createLinuxSpecifics()
920 983
921 # Create Desktop and Start Menu entries for Windows systems 984 # Create Desktop and Start Menu entries for Windows systems
922 elif sys.platform.startswith(("win", "cygwin")): 985 elif sys.platform.startswith(("win", "cygwin")):
923 createWindowsLinks() 986 createWindowsLinks()
924 987
925 # Create a Mac application bundle 988 # Create a Mac application bundle
926 elif sys.platform == "darwin": 989 elif sys.platform == "darwin":
927 createMacAppBundle(cfg['ericDir']) 990 createMacAppBundle(cfg["ericDir"])
928 991
929 return 0 992 return 0
930 993
931 994
932 def createLinuxSpecifics(): 995 def createLinuxSpecifics():
933 """ 996 """
934 Install Linux specific files. 997 Install Linux specific files.
935 """ 998 """
936 global distDir, sourceDir 999 global distDir, sourceDir
937 1000
938 dataSourceDir = os.path.join(eric7SourceDir, "data", "linux") 1001 dataSourceDir = os.path.join(eric7SourceDir, "data", "linux")
939 1002
940 if distDir: 1003 if distDir:
941 dst = os.path.normpath(os.path.join(distDir, "usr/share/icons")) 1004 dst = os.path.normpath(os.path.join(distDir, "usr/share/icons"))
942 if not os.path.exists(dst): 1005 if not os.path.exists(dst):
943 os.makedirs(dst) 1006 os.makedirs(dst)
944 shutilCopy( 1007 shutilCopy(
945 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"), 1008 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"),
946 os.path.join(dst, "eric.png")) 1009 os.path.join(dst, "eric.png"),
1010 )
947 shutilCopy( 1011 shutilCopy(
948 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1012 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
949 os.path.join(dst, "ericWeb.png")) 1013 os.path.join(dst, "ericWeb.png"),
950 1014 )
1015
951 dst = os.path.normpath( 1016 dst = os.path.normpath(
952 os.path.join(distDir, "usr/share/icons/hicolor/48x48/apps")) 1017 os.path.join(distDir, "usr/share/icons/hicolor/48x48/apps")
1018 )
953 if not os.path.exists(dst): 1019 if not os.path.exists(dst):
954 os.makedirs(dst) 1020 os.makedirs(dst)
955 shutilCopy( 1021 shutilCopy(
956 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"), 1022 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"),
957 os.path.join(dst, "eric.png")) 1023 os.path.join(dst, "eric.png"),
1024 )
958 shutilCopy( 1025 shutilCopy(
959 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1026 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
960 os.path.join(dst, "ericWeb.png")) 1027 os.path.join(dst, "ericWeb.png"),
961 1028 )
962 dst = os.path.normpath( 1029
963 os.path.join(distDir, "usr/share/applications")) 1030 dst = os.path.normpath(os.path.join(distDir, "usr/share/applications"))
964 if not os.path.exists(dst): 1031 if not os.path.exists(dst):
965 os.makedirs(dst) 1032 os.makedirs(dst)
966 copyDesktopFile(os.path.join(dataSourceDir, "eric7.desktop.in"), 1033 copyDesktopFile(
967 os.path.join(dst, "eric7.desktop")) 1034 os.path.join(dataSourceDir, "eric7.desktop.in"),
1035 os.path.join(dst, "eric7.desktop"),
1036 )
968 copyDesktopFile( 1037 copyDesktopFile(
969 os.path.join(dataSourceDir, "eric7_browser.desktop.in"), 1038 os.path.join(dataSourceDir, "eric7_browser.desktop.in"),
970 os.path.join(dst, "eric7_browser.desktop")) 1039 os.path.join(dst, "eric7_browser.desktop"),
971 1040 )
972 dst = os.path.normpath( 1041
973 os.path.join(distDir, "usr/share/metainfo")) 1042 dst = os.path.normpath(os.path.join(distDir, "usr/share/metainfo"))
974 if not os.path.exists(dst): 1043 if not os.path.exists(dst):
975 os.makedirs(dst) 1044 os.makedirs(dst)
976 copyAppStreamFile( 1045 copyAppStreamFile(
977 os.path.join(dataSourceDir, "eric7.appdata.xml.in"), 1046 os.path.join(dataSourceDir, "eric7.appdata.xml.in"),
978 os.path.join(dst, "eric7.appdata.xml")) 1047 os.path.join(dst, "eric7.appdata.xml"),
1048 )
979 elif os.getuid() == 0: 1049 elif os.getuid() == 0:
980 shutilCopy( 1050 shutilCopy(
981 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"), 1051 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"),
982 "/usr/share/icons/eric.png") 1052 "/usr/share/icons/eric.png",
1053 )
983 shutilCopy( 1054 shutilCopy(
984 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"), 1055 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"),
985 "/usr/share/icons/hicolor/48x48/apps/eric.png") 1056 "/usr/share/icons/hicolor/48x48/apps/eric.png",
1057 )
986 copyDesktopFile( 1058 copyDesktopFile(
987 os.path.join(dataSourceDir, "eric7.desktop.in"), 1059 os.path.join(dataSourceDir, "eric7.desktop.in"),
988 "/usr/share/applications/eric7.desktop") 1060 "/usr/share/applications/eric7.desktop",
1061 )
989 if os.path.exists("/usr/share/metainfo"): 1062 if os.path.exists("/usr/share/metainfo"):
990 copyAppStreamFile( 1063 copyAppStreamFile(
991 os.path.join(dataSourceDir, "eric7.appdata.xml.in"), 1064 os.path.join(dataSourceDir, "eric7.appdata.xml.in"),
992 "/usr/share/metainfo/eric7.appdata.xml") 1065 "/usr/share/metainfo/eric7.appdata.xml",
1066 )
993 elif os.path.exists("/usr/share/appdata"): 1067 elif os.path.exists("/usr/share/appdata"):
994 copyAppStreamFile( 1068 copyAppStreamFile(
995 os.path.join(dataSourceDir, "eric7.appdata.xml.in"), 1069 os.path.join(dataSourceDir, "eric7.appdata.xml.in"),
996 "/usr/share/appdata/eric7.appdata.xml") 1070 "/usr/share/appdata/eric7.appdata.xml",
1071 )
997 shutilCopy( 1072 shutilCopy(
998 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1073 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
999 "/usr/share/icons/ericWeb.png") 1074 "/usr/share/icons/ericWeb.png",
1075 )
1000 shutilCopy( 1076 shutilCopy(
1001 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1077 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
1002 "/usr/share/icons/hicolor/48x48/apps/ericWeb.png") 1078 "/usr/share/icons/hicolor/48x48/apps/ericWeb.png",
1079 )
1003 copyDesktopFile( 1080 copyDesktopFile(
1004 os.path.join(dataSourceDir, "eric7_browser.desktop.in"), 1081 os.path.join(dataSourceDir, "eric7_browser.desktop.in"),
1005 "/usr/share/applications/eric7_browser.desktop") 1082 "/usr/share/applications/eric7_browser.desktop",
1083 )
1006 elif os.getuid() >= 1000: 1084 elif os.getuid() >= 1000:
1007 # it is assumed, that user ids start at 1000 1085 # it is assumed, that user ids start at 1000
1008 localPath = os.path.join(os.path.expanduser("~"), 1086 localPath = os.path.join(os.path.expanduser("~"), ".local", "share")
1009 ".local", "share")
1010 # create directories first 1087 # create directories first
1011 for directory in [os.path.join(localPath, name) 1088 for directory in [
1012 for name in ("icons", "icons/hicolor/48x48/apps", 1089 os.path.join(localPath, name)
1013 "applications", "metainfo", "appdata")]: 1090 for name in (
1091 "icons",
1092 "icons/hicolor/48x48/apps",
1093 "applications",
1094 "metainfo",
1095 "appdata",
1096 )
1097 ]:
1014 if not os.path.isdir(directory): 1098 if not os.path.isdir(directory):
1015 os.makedirs(directory) 1099 os.makedirs(directory)
1016 # now copy the files 1100 # now copy the files
1017 shutilCopy( 1101 shutilCopy(
1018 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"), 1102 os.path.join(eric7SourceDir, "pixmaps", "eric_icon.png"),
1019 os.path.join(localPath, "icons", "eric.png")) 1103 os.path.join(localPath, "icons", "eric.png"),
1104 )
1020 shutilCopy( 1105 shutilCopy(
1021 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"), 1106 os.path.join(eric7SourceDir, "pixmaps", "eric48_icon.png"),
1022 os.path.join(localPath, "icons/hicolor/48x48/apps", "eric.png")) 1107 os.path.join(localPath, "icons/hicolor/48x48/apps", "eric.png"),
1108 )
1023 copyDesktopFile( 1109 copyDesktopFile(
1024 os.path.join(dataSourceDir, "eric7.desktop.in"), 1110 os.path.join(dataSourceDir, "eric7.desktop.in"),
1025 os.path.join(localPath, "applications", "eric7.desktop")) 1111 os.path.join(localPath, "applications", "eric7.desktop"),
1112 )
1026 copyAppStreamFile( 1113 copyAppStreamFile(
1027 os.path.join(dataSourceDir, "eric7.appdata.xml.in"), 1114 os.path.join(dataSourceDir, "eric7.appdata.xml.in"),
1028 os.path.join(localPath, "metainfo", "eric7.appdata.xml")) 1115 os.path.join(localPath, "metainfo", "eric7.appdata.xml"),
1116 )
1029 copyAppStreamFile( 1117 copyAppStreamFile(
1030 os.path.join(dataSourceDir, "eric7.appdata.xml.in"), 1118 os.path.join(dataSourceDir, "eric7.appdata.xml.in"),
1031 os.path.join(localPath, "appdata", "eric7.appdata.xml")) 1119 os.path.join(localPath, "appdata", "eric7.appdata.xml"),
1120 )
1032 shutilCopy( 1121 shutilCopy(
1033 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1122 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
1034 os.path.join(localPath, "icons", "ericWeb.png")) 1123 os.path.join(localPath, "icons", "ericWeb.png"),
1124 )
1035 shutilCopy( 1125 shutilCopy(
1036 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"), 1126 os.path.join(eric7SourceDir, "pixmaps", "ericWeb48_icon.png"),
1037 os.path.join(localPath, "icons/hicolor/48x48/apps", "ericWeb.png")) 1127 os.path.join(localPath, "icons/hicolor/48x48/apps", "ericWeb.png"),
1128 )
1038 copyDesktopFile( 1129 copyDesktopFile(
1039 os.path.join(dataSourceDir, "eric7_browser.desktop.in"), 1130 os.path.join(dataSourceDir, "eric7_browser.desktop.in"),
1040 os.path.join(localPath, "applications", 1131 os.path.join(localPath, "applications", "eric7_browser.desktop"),
1041 "eric7_browser.desktop")) 1132 )
1042 1133
1043 1134
1044 def createWindowsLinks(): 1135 def createWindowsLinks():
1045 """ 1136 """
1046 Create Desktop and Start Menu links. 1137 Create Desktop and Start Menu links.
1047 """ 1138 """
1048 try: 1139 try:
1049 # check, if pywin32 is available 1140 # check, if pywin32 is available
1050 from win32com.client import Dispatch # __IGNORE_WARNING__ 1141 from win32com.client import Dispatch # __IGNORE_WARNING__
1051 except ImportError: 1142 except ImportError:
1052 installed = pipInstall( 1143 installed = pipInstall(
1053 "pywin32", 1144 "pywin32",
1054 "\nThe Python package 'pywin32' could not be imported.", 1145 "\nThe Python package 'pywin32' could not be imported.",
1055 force=False 1146 force=False,
1056 ) 1147 )
1057 if installed: 1148 if installed:
1058 # create the links via an external script to get around some 1149 # create the links via an external script to get around some
1059 # startup magic done by pywin32.pth 1150 # startup magic done by pywin32.pth
1060 args = [ 1151 args = [
1061 sys.executable, 1152 sys.executable,
1062 os.path.join(os.path.dirname(__file__), 1153 os.path.join(os.path.dirname(__file__), "create_windows_links.py"),
1063 "create_windows_links.py"),
1064 ] 1154 ]
1065 subprocess.run(args) # secok 1155 subprocess.run(args) # secok
1066 else: 1156 else:
1067 print( 1157 print(
1068 "\nThe Python package 'pywin32' is not installed. Desktop and" 1158 "\nThe Python package 'pywin32' is not installed. Desktop and"
1069 " Start Menu entries will not be created." 1159 " Start Menu entries will not be created."
1070 ) 1160 )
1071 return 1161 return
1072 1162
1073 regPath = ( 1163 regPath = (
1074 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" + 1164 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"
1075 "\\User Shell Folders" 1165 + "\\User Shell Folders"
1076 ) 1166 )
1077 1167
1078 # 1. create desktop shortcuts 1168 # 1. create desktop shortcuts
1079 regName = "Desktop" 1169 regName = "Desktop"
1080 desktopEntry = getWinregEntry(regName, regPath) 1170 desktopEntry = getWinregEntry(regName, regPath)
1081 if desktopEntry: 1171 if desktopEntry:
1082 desktopFolder = os.path.normpath(os.path.expandvars(desktopEntry)) 1172 desktopFolder = os.path.normpath(os.path.expandvars(desktopEntry))
1083 for linkName, targetPath, iconPath in windowsDesktopEntries(): 1173 for linkName, targetPath, iconPath in windowsDesktopEntries():
1084 linkPath = os.path.join(desktopFolder, linkName) 1174 linkPath = os.path.join(desktopFolder, linkName)
1085 createWindowsShortcut(linkPath, targetPath, iconPath) 1175 createWindowsShortcut(linkPath, targetPath, iconPath)
1086 1176
1087 # 2. create start menu entry and shortcuts 1177 # 2. create start menu entry and shortcuts
1088 regName = "Programs" 1178 regName = "Programs"
1089 programsEntry = getWinregEntry(regName, regPath) 1179 programsEntry = getWinregEntry(regName, regPath)
1090 if programsEntry: 1180 if programsEntry:
1091 programsFolder = os.path.normpath(os.path.expandvars(programsEntry)) 1181 programsFolder = os.path.normpath(os.path.expandvars(programsEntry))
1094 try: 1184 try:
1095 os.makedirs(eric7EntryPath) 1185 os.makedirs(eric7EntryPath)
1096 except OSError: 1186 except OSError:
1097 # maybe restrictions prohibited link creation 1187 # maybe restrictions prohibited link creation
1098 return 1188 return
1099 1189
1100 for linkName, targetPath, iconPath in windowsDesktopEntries(): 1190 for linkName, targetPath, iconPath in windowsDesktopEntries():
1101 linkPath = os.path.join(eric7EntryPath, linkName) 1191 linkPath = os.path.join(eric7EntryPath, linkName)
1102 createWindowsShortcut(linkPath, targetPath, iconPath) 1192 createWindowsShortcut(linkPath, targetPath, iconPath)
1103 1193
1104 1194
1109 @param pydir the name of the directory where the Python script will 1199 @param pydir the name of the directory where the Python script will
1110 eventually be installed 1200 eventually be installed
1111 @type str 1201 @type str
1112 """ 1202 """
1113 global cfg, macAppBundleName, macPythonExe, macAppBundlePath 1203 global cfg, macAppBundleName, macPythonExe, macAppBundlePath
1114 1204
1115 directories = { 1205 directories = {
1116 "contents": "{0}/{1}/Contents/".format( 1206 "contents": "{0}/{1}/Contents/".format(macAppBundlePath, macAppBundleName),
1117 macAppBundlePath, macAppBundleName), 1207 "exe": "{0}/{1}/Contents/MacOS".format(macAppBundlePath, macAppBundleName),
1118 "exe": "{0}/{1}/Contents/MacOS".format( 1208 "icns": "{0}/{1}/Contents/Resources".format(macAppBundlePath, macAppBundleName),
1119 macAppBundlePath, macAppBundleName),
1120 "icns": "{0}/{1}/Contents/Resources".format(
1121 macAppBundlePath, macAppBundleName)
1122 } 1209 }
1123 for directory in directories.values(): 1210 for directory in directories.values():
1124 if not os.path.exists(directory): 1211 if not os.path.exists(directory):
1125 os.makedirs(directory) 1212 os.makedirs(directory)
1126 1213
1127 if macPythonExe == defaultMacPythonExe and macPythonExe: 1214 if macPythonExe == defaultMacPythonExe and macPythonExe:
1128 starter = os.path.join(directories["exe"], "eric") 1215 starter = os.path.join(directories["exe"], "eric")
1129 os.symlink(macPythonExe, starter) 1216 os.symlink(macPythonExe, starter)
1130 else: 1217 else:
1131 starter = "python{0}".format(sys.version_info.major) 1218 starter = "python{0}".format(sys.version_info.major)
1132 1219
1133 wname = os.path.join(directories["exe"], "eric7") 1220 wname = os.path.join(directories["exe"], "eric7")
1134 1221
1135 # determine entry for DYLD_FRAMEWORK_PATH 1222 # determine entry for DYLD_FRAMEWORK_PATH
1136 dyldLine = "" 1223 dyldLine = ""
1137 try: 1224 try:
1138 from PyQt6.QtCore import QLibraryInfo 1225 from PyQt6.QtCore import QLibraryInfo
1139 qtLibraryDir = QLibraryInfo.path( 1226
1140 QLibraryInfo.LibraryPath.LibrariesPath) 1227 qtLibraryDir = QLibraryInfo.path(QLibraryInfo.LibraryPath.LibrariesPath)
1141 except ImportError: 1228 except ImportError:
1142 qtLibraryDir = "" 1229 qtLibraryDir = ""
1143 if qtLibraryDir: 1230 if qtLibraryDir:
1144 dyldLine = "DYLD_FRAMEWORK_PATH={0}\n".format(qtLibraryDir) 1231 dyldLine = "DYLD_FRAMEWORK_PATH={0}\n".format(qtLibraryDir)
1145 1232
1146 # determine entry for PATH 1233 # determine entry for PATH
1147 pathLine = "" 1234 pathLine = ""
1148 path = os.getenv("PATH", "") 1235 path = os.getenv("PATH", "")
1149 if path: 1236 if path:
1150 pybin = os.path.join(sys.exec_prefix, "bin") 1237 pybin = os.path.join(sys.exec_prefix, "bin")
1152 pathlist_n = [pybin] 1239 pathlist_n = [pybin]
1153 for path_ in pathlist: 1240 for path_ in pathlist:
1154 if path_ and path_ not in pathlist_n: 1241 if path_ and path_ not in pathlist_n:
1155 pathlist_n.append(path_) 1242 pathlist_n.append(path_)
1156 pathLine = "PATH={0}\n".format(os.pathsep.join(pathlist_n)) 1243 pathLine = "PATH={0}\n".format(os.pathsep.join(pathlist_n))
1157 1244
1158 # create the wrapper script 1245 # create the wrapper script
1159 wrapper = ('''#!/bin/sh\n''' 1246 wrapper = (
1160 '''\n''' 1247 """#!/bin/sh\n"""
1161 '''{0}''' 1248 """\n"""
1162 '''{1}''' 1249 """{0}"""
1163 '''exec "{2}" "{3}/{4}.py" "$@"\n''' 1250 """{1}"""
1164 .format(pathLine, dyldLine, starter, pydir, "eric7")) 1251 """exec "{2}" "{3}/{4}.py" "$@"\n""".format(
1252 pathLine, dyldLine, starter, pydir, "eric7"
1253 )
1254 )
1165 copyToFile(wname, wrapper) 1255 copyToFile(wname, wrapper)
1166 os.chmod(wname, 0o755) # secok 1256 os.chmod(wname, 0o755) # secok
1167 1257
1168 shutilCopy(os.path.join(eric7SourceDir, "pixmaps", "eric_2.icns"), 1258 shutilCopy(
1169 os.path.join(directories["icns"], "eric.icns")) 1259 os.path.join(eric7SourceDir, "pixmaps", "eric_2.icns"),
1170 1260 os.path.join(directories["icns"], "eric.icns"),
1261 )
1262
1171 if os.path.exists(os.path.join("eric", "eric7", "UI", "Info.py")): 1263 if os.path.exists(os.path.join("eric", "eric7", "UI", "Info.py")):
1172 # Installing from archive 1264 # Installing from archive
1173 from eric.eric7.UI.Info import Version, CopyrightShort 1265 from eric.eric7.UI.Info import Version, CopyrightShort
1174 elif os.path.exists(os.path.join("eric7", "UI", "Info.py")): 1266 elif os.path.exists(os.path.join("eric7", "UI", "Info.py")):
1175 # Installing from source tree 1267 # Installing from source tree
1176 from eric7.UI.Info import Version, CopyrightShort 1268 from eric7.UI.Info import Version, CopyrightShort
1177 else: 1269 else:
1178 Version = "Unknown" 1270 Version = "Unknown"
1179 CopyrightShort = "(c) 2002 - 2022 Detlev Offenbach" 1271 CopyrightShort = "(c) 2002 - 2022 Detlev Offenbach"
1180 1272
1181 copyToFile( 1273 copyToFile(
1182 os.path.join(directories["contents"], "Info.plist"), 1274 os.path.join(directories["contents"], "Info.plist"),
1183 '''<?xml version="1.0" encoding="UTF-8"?>\n''' 1275 """<?xml version="1.0" encoding="UTF-8"?>\n"""
1184 '''<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"\n''' 1276 """<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"\n"""
1185 ''' "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n''' 1277 """ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n"""
1186 '''<plist version="1.0">\n''' 1278 """<plist version="1.0">\n"""
1187 '''<dict>\n''' 1279 """<dict>\n"""
1188 ''' <key>CFBundleExecutable</key>\n''' 1280 """ <key>CFBundleExecutable</key>\n"""
1189 ''' <string>eric7</string>\n''' 1281 """ <string>eric7</string>\n"""
1190 ''' <key>CFBundleIconFile</key>\n''' 1282 """ <key>CFBundleIconFile</key>\n"""
1191 ''' <string>eric.icns</string>\n''' 1283 """ <string>eric.icns</string>\n"""
1192 ''' <key>CFBundleInfoDictionaryVersion</key>\n''' 1284 """ <key>CFBundleInfoDictionaryVersion</key>\n"""
1193 ''' <string>{1}</string>\n''' 1285 """ <string>{1}</string>\n"""
1194 ''' <key>CFBundleName</key>\n''' 1286 """ <key>CFBundleName</key>\n"""
1195 ''' <string>{0}</string>\n''' 1287 """ <string>{0}</string>\n"""
1196 ''' <key>CFBundleDisplayName</key>\n''' 1288 """ <key>CFBundleDisplayName</key>\n"""
1197 ''' <string>{0}</string>\n''' 1289 """ <string>{0}</string>\n"""
1198 ''' <key>CFBundlePackageType</key>\n''' 1290 """ <key>CFBundlePackageType</key>\n"""
1199 ''' <string>APPL</string>\n''' 1291 """ <string>APPL</string>\n"""
1200 ''' <key>CFBundleSignature</key>\n''' 1292 """ <key>CFBundleSignature</key>\n"""
1201 ''' <string>ERIC-IDE</string>\n''' 1293 """ <string>ERIC-IDE</string>\n"""
1202 ''' <key>CFBundleVersion</key>\n''' 1294 """ <key>CFBundleVersion</key>\n"""
1203 ''' <string>{1}</string>\n''' 1295 """ <string>{1}</string>\n"""
1204 ''' <key>CFBundleGetInfoString</key>\n''' 1296 """ <key>CFBundleGetInfoString</key>\n"""
1205 ''' <string>{1}, {2}</string>\n''' 1297 """ <string>{1}, {2}</string>\n"""
1206 ''' <key>CFBundleIdentifier</key>\n''' 1298 """ <key>CFBundleIdentifier</key>\n"""
1207 ''' <string>org.python-projects.eric-ide</string>\n''' 1299 """ <string>org.python-projects.eric-ide</string>\n"""
1208 ''' <key>NSRequiresAquaSystemAppearance</key>\n''' 1300 """ <key>NSRequiresAquaSystemAppearance</key>\n"""
1209 ''' <string>false</string>\n''' 1301 """ <string>false</string>\n"""
1210 ''' <key>LSEnvironment</key>\n''' 1302 """ <key>LSEnvironment</key>\n"""
1211 ''' <dict>\n''' 1303 """ <dict>\n"""
1212 ''' <key>LANG</key>\n''' 1304 """ <key>LANG</key>\n"""
1213 ''' <string>en_US.UTF-8</string>\n''' 1305 """ <string>en_US.UTF-8</string>\n"""
1214 ''' <key>LC_ALL</key>\n''' 1306 """ <key>LC_ALL</key>\n"""
1215 ''' <string>en_US.UTF-8</string>\n''' 1307 """ <string>en_US.UTF-8</string>\n"""
1216 ''' <key>LC_CTYPE</key>\n''' 1308 """ <key>LC_CTYPE</key>\n"""
1217 ''' <string>en_US.UTF-8</string>\n''' 1309 """ <string>en_US.UTF-8</string>\n"""
1218 ''' </dict>\n''' 1310 """ </dict>\n"""
1219 '''</dict>\n''' 1311 """</dict>\n"""
1220 '''</plist>\n'''.format( 1312 """</plist>\n""".format(
1221 macAppBundleName.replace(".app", ""), 1313 macAppBundleName.replace(".app", ""),
1222 Version.split(None, 1)[0], 1314 Version.split(None, 1)[0],
1223 CopyrightShort)) 1315 CopyrightShort,
1316 ),
1317 )
1224 1318
1225 1319
1226 def createInstallConfig(): 1320 def createInstallConfig():
1227 """ 1321 """
1228 Create the installation config dictionary. 1322 Create the installation config dictionary.
1229 """ 1323 """
1230 global modDir, platBinDir, cfg, apisDir, installApis 1324 global modDir, platBinDir, cfg, apisDir, installApis
1231 1325
1232 ericdir = os.path.join(modDir, "eric7") 1326 ericdir = os.path.join(modDir, "eric7")
1233 cfg = { 1327 cfg = {
1234 'ericDir': ericdir, 1328 "ericDir": ericdir,
1235 'ericPixDir': os.path.join(ericdir, "pixmaps"), 1329 "ericPixDir": os.path.join(ericdir, "pixmaps"),
1236 'ericIconDir': os.path.join(ericdir, "icons"), 1330 "ericIconDir": os.path.join(ericdir, "icons"),
1237 'ericDTDDir': os.path.join(ericdir, "DTDs"), 1331 "ericDTDDir": os.path.join(ericdir, "DTDs"),
1238 'ericCSSDir': os.path.join(ericdir, "CSSs"), 1332 "ericCSSDir": os.path.join(ericdir, "CSSs"),
1239 'ericStylesDir': os.path.join(ericdir, "Styles"), 1333 "ericStylesDir": os.path.join(ericdir, "Styles"),
1240 'ericThemesDir': os.path.join(ericdir, "Themes"), 1334 "ericThemesDir": os.path.join(ericdir, "Themes"),
1241 'ericDocDir': os.path.join(ericdir, "Documentation"), 1335 "ericDocDir": os.path.join(ericdir, "Documentation"),
1242 'ericExamplesDir': os.path.join(ericdir, "Examples"), 1336 "ericExamplesDir": os.path.join(ericdir, "Examples"),
1243 'ericTranslationsDir': os.path.join(ericdir, "i18n"), 1337 "ericTranslationsDir": os.path.join(ericdir, "i18n"),
1244 'ericTemplatesDir': os.path.join(ericdir, "DesignerTemplates"), 1338 "ericTemplatesDir": os.path.join(ericdir, "DesignerTemplates"),
1245 'ericCodeTemplatesDir': os.path.join(ericdir, 'CodeTemplates'), 1339 "ericCodeTemplatesDir": os.path.join(ericdir, "CodeTemplates"),
1246 'ericOthersDir': ericdir, 1340 "ericOthersDir": ericdir,
1247 'bindir': platBinDir, 1341 "bindir": platBinDir,
1248 'mdir': modDir, 1342 "mdir": modDir,
1249 } 1343 }
1250 if installApis: 1344 if installApis:
1251 if apisDir: 1345 if apisDir:
1252 cfg['apidir'] = apisDir 1346 cfg["apidir"] = apisDir
1253 else: 1347 else:
1254 cfg['apidir'] = os.path.join(ericdir, "api") 1348 cfg["apidir"] = os.path.join(ericdir, "api")
1255 else: 1349 else:
1256 cfg['apidir'] = "" 1350 cfg["apidir"] = ""
1351
1352
1257 configLength = 16 1353 configLength = 16
1258 1354
1259 1355
1260 def createConfig(): 1356 def createConfig():
1261 """ 1357 """
1262 Create a config file with the respective config entries. 1358 Create a config file with the respective config entries.
1263 """ 1359 """
1264 global cfg, macAppBundlePath, configName 1360 global cfg, macAppBundlePath, configName
1265 1361
1266 apis = [] 1362 apis = []
1267 if installApis: 1363 if installApis:
1268 for progLanguage in progLanguages: 1364 for progLanguage in progLanguages:
1269 for apiName in sorted( 1365 for apiName in sorted(
1270 glob.glob(os.path.join(eric7SourceDir, "APIs", progLanguage, 1366 glob.glob(os.path.join(eric7SourceDir, "APIs", progLanguage, "*.api"))
1271 "*.api"))): 1367 ):
1272 apis.append(os.path.basename(apiName)) 1368 apis.append(os.path.basename(apiName))
1273 if progLanguage == "Python": 1369 if progLanguage == "Python":
1274 # treat Python3 API files the same as Python API files 1370 # treat Python3 API files the same as Python API files
1275 for apiName in sorted( 1371 for apiName in sorted(
1276 glob.glob(os.path.join(eric7SourceDir, "APIs", "Python3", 1372 glob.glob(os.path.join(eric7SourceDir, "APIs", "Python3", "*.api"))
1277 "*.api"))): 1373 ):
1278 apis.append(os.path.basename(apiName)) 1374 apis.append(os.path.basename(apiName))
1279 1375
1280 # treat MicroPython API files the same as Python API files 1376 # treat MicroPython API files the same as Python API files
1281 for apiName in sorted( 1377 for apiName in sorted(
1282 glob.glob(os.path.join(eric7SourceDir, "APIs", 1378 glob.glob(
1283 "MicroPython", "*.api"))): 1379 os.path.join(eric7SourceDir, "APIs", "MicroPython", "*.api")
1380 )
1381 ):
1284 apis.append(os.path.basename(apiName)) 1382 apis.append(os.path.basename(apiName))
1285 1383
1286 macConfig = ( 1384 macConfig = (
1287 (""" 'macAppBundlePath': r'{0}',\n""" 1385 (
1288 """ 'macAppBundleName': r'{1}',\n""").format(macAppBundlePath, 1386 """ 'macAppBundlePath': r'{0}',\n"""
1289 macAppBundleName) 1387 """ 'macAppBundleName': r'{1}',\n"""
1290 if sys.platform == "darwin" else 1388 ).format(macAppBundlePath, macAppBundleName)
1291 "" 1389 if sys.platform == "darwin"
1390 else ""
1292 ) 1391 )
1293 config = ( 1392 config = (
1294 """# -*- coding: utf-8 -*-\n""" 1393 """# -*- coding: utf-8 -*-\n"""
1295 """#\n""" 1394 """#\n"""
1296 """# This module contains the configuration of the individual eric""" 1395 """# This module contains the configuration of the individual eric"""
1334 """\n""" 1433 """\n"""
1335 """ raise AttributeError(\n""" 1434 """ raise AttributeError(\n"""
1336 """ '"{{0}}" is not a valid configuration value'""" 1435 """ '"{{0}}" is not a valid configuration value'"""
1337 """.format(name))\n""" 1436 """.format(name))\n"""
1338 ).format( 1437 ).format(
1339 cfg['ericDir'], cfg['ericPixDir'], cfg['ericIconDir'], 1438 cfg["ericDir"],
1340 cfg['ericDTDDir'], cfg['ericCSSDir'], 1439 cfg["ericPixDir"],
1341 cfg['ericStylesDir'], cfg['ericThemesDir'], cfg['ericDocDir'], 1440 cfg["ericIconDir"],
1342 cfg['ericExamplesDir'], cfg['ericTranslationsDir'], 1441 cfg["ericDTDDir"],
1343 cfg['ericTemplatesDir'], 1442 cfg["ericCSSDir"],
1344 cfg['ericCodeTemplatesDir'], cfg['ericOthersDir'], 1443 cfg["ericStylesDir"],
1345 cfg['bindir'], cfg['mdir'], 1444 cfg["ericThemesDir"],
1346 cfg['apidir'], sorted(apis), 1445 cfg["ericDocDir"],
1446 cfg["ericExamplesDir"],
1447 cfg["ericTranslationsDir"],
1448 cfg["ericTemplatesDir"],
1449 cfg["ericCodeTemplatesDir"],
1450 cfg["ericOthersDir"],
1451 cfg["bindir"],
1452 cfg["mdir"],
1453 cfg["apidir"],
1454 sorted(apis),
1347 macConfig, 1455 macConfig,
1348 ) 1456 )
1349 copyToFile(configName, config) 1457 copyToFile(configName, config)
1350 1458
1351 1459
1352 def createInstallInfo(): 1460 def createInstallInfo():
1353 """ 1461 """
1354 Record information about the way eric was installed. 1462 Record information about the way eric was installed.
1355 """ 1463 """
1356 global createInstallInfoFile, installInfo, installCwd, cfg 1464 global createInstallInfoFile, installInfo, installCwd, cfg
1357 1465
1358 if createInstallInfoFile: 1466 if createInstallInfoFile:
1359 installDateTime = datetime.datetime.now(tz=None) 1467 installDateTime = datetime.datetime.now(tz=None)
1360 try: 1468 try:
1361 installInfo["sudo"] = os.getuid() == 0 1469 installInfo["sudo"] = os.getuid() == 0
1362 except AttributeError: 1470 except AttributeError:
1365 installInfo["exe"] = sys.executable 1473 installInfo["exe"] = sys.executable
1366 installInfo["argv"] = " ".join(shlex.quote(a) for a in sys.argv[:]) 1474 installInfo["argv"] = " ".join(shlex.quote(a) for a in sys.argv[:])
1367 installInfo["install_cwd"] = installCwd 1475 installInfo["install_cwd"] = installCwd
1368 installInfo["eric"] = cfg["ericDir"] 1476 installInfo["eric"] = cfg["ericDir"]
1369 installInfo["virtualenv"] = installInfo["eric"].startswith( 1477 installInfo["virtualenv"] = installInfo["eric"].startswith(
1370 os.path.expanduser("~")) 1478 os.path.expanduser("~")
1479 )
1371 installInfo["installed"] = True 1480 installInfo["installed"] = True
1372 installInfo["installed_on"] = installDateTime.strftime( 1481 installInfo["installed_on"] = installDateTime.strftime("%Y-%m-%d %H:%M:%S")
1373 "%Y-%m-%d %H:%M:%S")
1374 installInfo["guessed"] = False 1482 installInfo["guessed"] = False
1375 installInfo["edited"] = False 1483 installInfo["edited"] = False
1376 installInfo["pip"] = False 1484 installInfo["pip"] = False
1377 installInfo["remarks"] = "" 1485 installInfo["remarks"] = ""
1378 installInfo["install_cwd_edited"] = False 1486 installInfo["install_cwd_edited"] = False
1382 1490
1383 1491
1384 def pipInstall(packageName, message, force=True): 1492 def pipInstall(packageName, message, force=True):
1385 """ 1493 """
1386 Install the given package via pip. 1494 Install the given package via pip.
1387 1495
1388 @param packageName name of the package to be installed 1496 @param packageName name of the package to be installed
1389 @type str 1497 @type str
1390 @param message message to be shown to the user 1498 @param message message to be shown to the user
1391 @type str 1499 @type str
1392 @param force flag indicating to perform the installation 1500 @param force flag indicating to perform the installation
1394 @type bool 1502 @type bool
1395 @return flag indicating a successful installation 1503 @return flag indicating a successful installation
1396 @rtype bool 1504 @rtype bool
1397 """ 1505 """
1398 global yes2All 1506 global yes2All
1399 1507
1400 ok = False 1508 ok = False
1401 if yes2All or force: 1509 if yes2All or force:
1402 answer = "y" 1510 answer = "y"
1403 else: 1511 else:
1404 print("{0}\nShall '{1}' be installed using pip? (Y/n)" 1512 print(
1405 .format(message, packageName), end=" ") 1513 "{0}\nShall '{1}' be installed using pip? (Y/n)".format(
1406 answer = input() # secok 1514 message, packageName
1515 ),
1516 end=" ",
1517 )
1518 answer = input() # secok
1407 if answer in ("", "Y", "y"): 1519 if answer in ("", "Y", "y"):
1408 exitCode = subprocess.run( # secok 1520 exitCode = subprocess.run( # secok
1409 [sys.executable, "-m", "pip", "install", "--prefer-binary", 1521 [
1410 "--upgrade", packageName] 1522 sys.executable,
1523 "-m",
1524 "pip",
1525 "install",
1526 "--prefer-binary",
1527 "--upgrade",
1528 packageName,
1529 ]
1411 ).returncode 1530 ).returncode
1412 ok = (exitCode == 0) 1531 ok = exitCode == 0
1413 1532
1414 return ok 1533 return ok
1415 1534
1416 1535
1417 def isPipOutdated(): 1536 def isPipOutdated():
1418 """ 1537 """
1419 Check, if pip is outdated. 1538 Check, if pip is outdated.
1420 1539
1421 @return flag indicating an outdated pip 1540 @return flag indicating an outdated pip
1422 @rtype bool 1541 @rtype bool
1423 """ 1542 """
1424 try: 1543 try:
1425 pipOut = subprocess.run( # secok 1544 pipOut = subprocess.run( # secok
1426 [sys.executable, "-m", "pip", "list", "--outdated", 1545 [sys.executable, "-m", "pip", "list", "--outdated", "--format=json"],
1427 "--format=json"], 1546 check=True,
1428 check=True, capture_output=True, text=True 1547 capture_output=True,
1548 text=True,
1429 ).stdout 1549 ).stdout
1430 except (OSError, subprocess.CalledProcessError): 1550 except (OSError, subprocess.CalledProcessError):
1431 pipOut = "[]" # default empty list 1551 pipOut = "[]" # default empty list
1432 try: 1552 try:
1433 jsonList = json.loads(pipOut) 1553 jsonList = json.loads(pipOut)
1434 except Exception: 1554 except Exception:
1435 jsonList = [] 1555 jsonList = []
1436 for package in jsonList: 1556 for package in jsonList:
1437 if isinstance(package, dict) and package["name"] == "pip": 1557 if isinstance(package, dict) and package["name"] == "pip":
1438 print("'pip' is outdated (installed {0}, available {1})".format( 1558 print(
1439 package["version"], package["latest_version"] 1559 "'pip' is outdated (installed {0}, available {1})".format(
1440 )) 1560 package["version"], package["latest_version"]
1561 )
1562 )
1441 return True 1563 return True
1442 1564
1443 return False 1565 return False
1444 1566
1445 1567
1446 def updatePip(): 1568 def updatePip():
1447 """ 1569 """
1448 Update the installed pip package. 1570 Update the installed pip package.
1449 """ 1571 """
1450 global yes2All 1572 global yes2All
1451 1573
1452 if yes2All: 1574 if yes2All:
1453 answer = "y" 1575 answer = "y"
1454 else: 1576 else:
1455 print("Shall 'pip' be updated (recommended)? (Y/n)", end=" ") 1577 print("Shall 'pip' be updated (recommended)? (Y/n)", end=" ")
1456 answer = input() # secok 1578 answer = input() # secok
1457 if answer in ("", "Y", "y"): 1579 if answer in ("", "Y", "y"):
1458 subprocess.run( # secok 1580 subprocess.run( # secok
1459 [sys.executable, "-m", "pip", "install", "--upgrade", "pip"]) 1581 [sys.executable, "-m", "pip", "install", "--upgrade", "pip"]
1582 )
1460 1583
1461 1584
1462 def versionToStr(version): 1585 def versionToStr(version):
1463 """ 1586 """
1464 Function to convert a version number into a version string. 1587 Function to convert a version number into a version string.
1465 1588
1466 @param version version number to convert 1589 @param version version number to convert
1467 @type int 1590 @type int
1468 @return version string 1591 @return version string
1469 @rtype str 1592 @rtype str
1470 """ 1593 """
1471 parts = [] 1594 parts = []
1472 while version: 1595 while version:
1473 parts.append(version & 0xff) 1596 parts.append(version & 0xFF)
1474 version >>= 8 1597 version >>= 8
1475 return '.'.join(str(p) for p in reversed(parts)) 1598 return ".".join(str(p) for p in reversed(parts))
1476 1599
1477 1600
1478 def doDependancyChecks(): 1601 def doDependancyChecks():
1479 """ 1602 """
1480 Perform some dependency checks. 1603 Perform some dependency checks.
1481 """ 1604 """
1482 global verbose 1605 global verbose
1483 1606
1484 requiredVersions = { 1607 requiredVersions = {
1485 "pyqt6": 0x60200, # v6.2.0 1608 "pyqt6": 0x60200, # v6.2.0
1486 "pyqt6-charts": 0x60200, # v6.2.0 1609 "pyqt6-charts": 0x60200, # v6.2.0
1487 "pyqt6-webengine": 0x60200, # v6.2.0 1610 "pyqt6-webengine": 0x60200, # v6.2.0
1488 "pyqt6-qscintilla": 0x20d00, # v2.13.0 1611 "pyqt6-qscintilla": 0x20D00, # v2.13.0
1489 "sip": 0x60100, # v6.1.0 1612 "sip": 0x60100, # v6.1.0
1490 } 1613 }
1491 1614
1492 try: 1615 try:
1493 isSudo = os.getuid() == 0 and sys.platform != "darwin" 1616 isSudo = os.getuid() == 0 and sys.platform != "darwin"
1494 # disregard sudo installs on macOS 1617 # disregard sudo installs on macOS
1495 except AttributeError: 1618 except AttributeError:
1496 isSudo = False 1619 isSudo = False
1497 1620
1498 print('Checking dependencies') 1621 print("Checking dependencies")
1499 1622
1500 # update pip first even if we don't need to install anything 1623 # update pip first even if we don't need to install anything
1501 if not isSudo and isPipOutdated(): 1624 if not isSudo and isPipOutdated():
1502 updatePip() 1625 updatePip()
1503 print("\n") 1626 print("\n")
1504 1627
1505 # perform dependency checks 1628 # perform dependency checks
1506 if sys.version_info < (3, 7, 0) or sys.version_info >= (3, 12, 0): 1629 if sys.version_info < (3, 7, 0) or sys.version_info >= (3, 12, 0):
1507 print('Sorry, you must have Python 3.7.0 or higher, but less 3.12.0.') 1630 print("Sorry, you must have Python 3.7.0 or higher, but less 3.12.0.")
1508 print("Yours is {0}.".format( 1631 print("Yours is {0}.".format(".".join(str(v) for v in sys.version_info[:3])))
1509 ".".join(str(v) for v in sys.version_info[:3])
1510 ))
1511 exit(5) 1632 exit(5)
1512 1633
1513 try: 1634 try:
1514 import xml.etree # __IGNORE_WARNING__ 1635 import xml.etree # __IGNORE_WARNING__
1515 except ImportError: 1636 except ImportError:
1516 print('Your Python installation is missing the XML module.') 1637 print("Your Python installation is missing the XML module.")
1517 print('Please install it and try again.') 1638 print("Please install it and try again.")
1518 exit(5) 1639 exit(5)
1519 1640
1520 try: 1641 try:
1521 from PyQt6.QtCore import qVersion 1642 from PyQt6.QtCore import qVersion
1522 except ImportError as err: 1643 except ImportError as err:
1523 msg = "'PyQt6' could not be detected.{0}".format( 1644 msg = "'PyQt6' could not be detected.{0}".format(
1524 "\nError: {0}".format(err) if verbose else "") 1645 "\nError: {0}".format(err) if verbose else ""
1646 )
1525 installed = not isSudo and pipInstall( 1647 installed = not isSudo and pipInstall(
1526 "PyQt6>={0}".format(versionToStr(requiredVersions["pyqt6"])), 1648 "PyQt6>={0}".format(versionToStr(requiredVersions["pyqt6"])), msg
1527 msg
1528 ) 1649 )
1529 if installed: 1650 if installed:
1530 # try to import it again 1651 # try to import it again
1531 try: 1652 try:
1532 from PyQt6.QtCore import qVersion 1653 from PyQt6.QtCore import qVersion
1533 except ImportError as msg: 1654 except ImportError as msg:
1534 print('Sorry, please install PyQt6.') 1655 print("Sorry, please install PyQt6.")
1535 print('Error: {0}'.format(msg)) 1656 print("Error: {0}".format(msg))
1536 exit(1) 1657 exit(1)
1537 else: 1658 else:
1538 print('Sorry, please install PyQt6.') 1659 print("Sorry, please install PyQt6.")
1539 print('Error: {0}'.format(msg)) 1660 print("Error: {0}".format(msg))
1540 exit(1) 1661 exit(1)
1541 print("Found PyQt6") 1662 print("Found PyQt6")
1542 1663
1543 try: 1664 try:
1544 pyuic = "pyuic6" 1665 pyuic = "pyuic6"
1545 from PyQt6 import uic # __IGNORE_WARNING__ 1666 from PyQt6 import uic # __IGNORE_WARNING__
1546 except ImportError as err: 1667 except ImportError as err:
1547 print("Sorry, {0} is not installed.".format(pyuic)) 1668 print("Sorry, {0} is not installed.".format(pyuic))
1548 if verbose: 1669 if verbose:
1549 print('Error: {0}'.format(err)) 1670 print("Error: {0}".format(err))
1550 exit(1) 1671 exit(1)
1551 print("Found {0}".format(pyuic)) 1672 print("Found {0}".format(pyuic))
1552 1673
1553 try: 1674 try:
1554 from PyQt6 import QtWebEngineWidgets # __IGNORE_WARNING__ 1675 from PyQt6 import QtWebEngineWidgets # __IGNORE_WARNING__
1555 except ImportError as err: 1676 except ImportError as err:
1556 if isSudo: 1677 if isSudo:
1557 print("Optional 'PyQt6-WebEngine' could not be detected.") 1678 print("Optional 'PyQt6-WebEngine' could not be detected.")
1558 else: 1679 else:
1559 msg = ( 1680 msg = "Optional 'PyQt6-WebEngine' could not be detected.{0}".format(
1560 "Optional 'PyQt6-WebEngine' could not be detected.{0}" 1681 "\nError: {0}".format(err) if verbose else ""
1561 .format("\nError: {0}".format(err) if verbose else "")
1562 ) 1682 )
1563 pipInstall( 1683 pipInstall(
1564 "PyQt6-WebEngine>={0}".format( 1684 "PyQt6-WebEngine>={0}".format(
1565 versionToStr(requiredVersions["pyqt6-webengine"])), 1685 versionToStr(requiredVersions["pyqt6-webengine"])
1566 msg 1686 ),
1687 msg,
1567 ) 1688 )
1568 1689
1569 try: 1690 try:
1570 from PyQt6 import QtCharts # __IGNORE_WARNING__ 1691 from PyQt6 import QtCharts # __IGNORE_WARNING__
1571 except ImportError as err: 1692 except ImportError as err:
1572 if isSudo: 1693 if isSudo:
1573 print("Optional 'PyQt6-Charts' could not be detected.") 1694 print("Optional 'PyQt6-Charts' could not be detected.")
1574 else: 1695 else:
1575 msg = "Optional 'PyQt6-Charts' could not be detected.{0}".format( 1696 msg = "Optional 'PyQt6-Charts' could not be detected.{0}".format(
1576 "\nError: {0}".format(err) if verbose else "") 1697 "\nError: {0}".format(err) if verbose else ""
1698 )
1577 pipInstall( 1699 pipInstall(
1578 "PyQt6-Charts>={0}".format( 1700 "PyQt6-Charts>={0}".format(
1579 versionToStr(requiredVersions["pyqt6-charts"])), 1701 versionToStr(requiredVersions["pyqt6-charts"])
1580 msg 1702 ),
1703 msg,
1581 ) 1704 )
1582 print("Found PyQt6-Charts") 1705 print("Found PyQt6-Charts")
1583 1706
1584 try: 1707 try:
1585 from PyQt6 import Qsci # __IGNORE_WARNING__ 1708 from PyQt6 import Qsci # __IGNORE_WARNING__
1586 except ImportError as err: 1709 except ImportError as err:
1587 msg = "'PyQt6-QScintilla' could not be detected.{0}".format( 1710 msg = "'PyQt6-QScintilla' could not be detected.{0}".format(
1588 "\nError: {0}".format(err) if verbose else "") 1711 "\nError: {0}".format(err) if verbose else ""
1712 )
1589 installed = not isSudo and pipInstall( 1713 installed = not isSudo and pipInstall(
1590 "PyQt6-QScintilla>={0}".format( 1714 "PyQt6-QScintilla>={0}".format(
1591 versionToStr(requiredVersions["pyqt6-qscintilla"])), 1715 versionToStr(requiredVersions["pyqt6-qscintilla"])
1592 msg 1716 ),
1717 msg,
1593 ) 1718 )
1594 if installed: 1719 if installed:
1595 # try to import it again 1720 # try to import it again
1596 try: 1721 try:
1597 from PyQt6 import Qsci # __IGNORE_WARNING__ 1722 from PyQt6 import Qsci # __IGNORE_WARNING__
1723
1598 message = None 1724 message = None
1599 except ImportError as msg: 1725 except ImportError as msg:
1600 message = str(msg) 1726 message = str(msg)
1601 else: 1727 else:
1602 message = "PyQt6-QScintilla could not be installed." 1728 message = "PyQt6-QScintilla could not be installed."
1603 if message: 1729 if message:
1604 print("Sorry, please install QScintilla2 and") 1730 print("Sorry, please install QScintilla2 and")
1605 print("its PyQt6 wrapper.") 1731 print("its PyQt6 wrapper.")
1606 print('Error: {0}'.format(message)) 1732 print("Error: {0}".format(message))
1607 exit(1) 1733 exit(1)
1608 print("Found PyQt6-QScintilla") 1734 print("Found PyQt6-QScintilla")
1609 1735
1610 impModulesList = [ 1736 impModulesList = [
1611 "PyQt6.QtGui", "PyQt6.QtNetwork", "PyQt6.QtPrintSupport", 1737 "PyQt6.QtGui",
1612 "PyQt6.QtSql", "PyQt6.QtSvg", "PyQt6.QtSvgWidgets", "PyQt6.QtWidgets", 1738 "PyQt6.QtNetwork",
1739 "PyQt6.QtPrintSupport",
1740 "PyQt6.QtSql",
1741 "PyQt6.QtSvg",
1742 "PyQt6.QtSvgWidgets",
1743 "PyQt6.QtWidgets",
1613 ] 1744 ]
1614 optionalModulesList = { 1745 optionalModulesList = {
1615 # key is pip project name 1746 # key is pip project name
1616 # value is tuple of package name, pip install constraint 1747 # value is tuple of package name, pip install constraint
1617 "docutils": ("docutils", ""), 1748 "docutils": ("docutils", ""),
1633 "trove-classifiers": ("trove_classifiers", ""), 1764 "trove-classifiers": ("trove_classifiers", ""),
1634 "black": ("black", ">=22.6.0"), 1765 "black": ("black", ">=22.6.0"),
1635 } 1766 }
1636 if not ignorePyqt6Tools: 1767 if not ignorePyqt6Tools:
1637 optionalModulesList["qt6-applications"] = ("qt6_applications", "") 1768 optionalModulesList["qt6-applications"] = ("qt6_applications", "")
1638 1769
1639 # check mandatory modules 1770 # check mandatory modules
1640 modulesOK = True 1771 modulesOK = True
1641 for impModule in impModulesList: 1772 for impModule in impModulesList:
1642 name = impModule.split(".")[1] 1773 name = impModule.split(".")[1]
1643 try: 1774 try:
1644 __import__(impModule) 1775 __import__(impModule)
1645 print("Found", name) 1776 print("Found", name)
1646 except ImportError as err: 1777 except ImportError as err:
1647 print('Sorry, please install {0}.'.format(name)) 1778 print("Sorry, please install {0}.".format(name))
1648 if verbose: 1779 if verbose:
1649 print('Error: {0}'.format(err)) 1780 print("Error: {0}".format(err))
1650 modulesOK = False 1781 modulesOK = False
1651 if not modulesOK: 1782 if not modulesOK:
1652 exit(1) 1783 exit(1)
1653 1784
1654 # check optional modules 1785 # check optional modules
1655 for optPackage in optionalModulesList: 1786 for optPackage in optionalModulesList:
1656 try: 1787 try:
1657 __import__(optionalModulesList[optPackage][0]) 1788 __import__(optionalModulesList[optPackage][0])
1658 print("Found", optPackage) 1789 print("Found", optPackage)
1659 except ImportError as err: 1790 except ImportError as err:
1660 if isSudo: 1791 if isSudo:
1661 print("Optional '{0}' could not be detected." 1792 print("Optional '{0}' could not be detected.".format(optPackage))
1662 .format(optPackage))
1663 else: 1793 else:
1664 msg = "Optional '{0}' could not be detected.{1}".format( 1794 msg = "Optional '{0}' could not be detected.{1}".format(
1665 optPackage, 1795 optPackage, "\nError: {0}".format(err) if verbose else ""
1666 "\nError: {0}".format(err) if verbose else ""
1667 ) 1796 )
1668 pipInstall( 1797 pipInstall(optPackage + optionalModulesList[optPackage][1], msg)
1669 optPackage + optionalModulesList[optPackage][1], 1798
1670 msg
1671 )
1672
1673 # determine the platform dependent black list 1799 # determine the platform dependent black list
1674 if sys.platform.startswith(("win", "cygwin")): 1800 if sys.platform.startswith(("win", "cygwin")):
1675 PlatformBlackLists = PlatformsBlackLists["windows"] 1801 PlatformBlackLists = PlatformsBlackLists["windows"]
1676 elif sys.platform.startswith("linux"): 1802 elif sys.platform.startswith("linux"):
1677 PlatformBlackLists = PlatformsBlackLists["linux"] 1803 PlatformBlackLists = PlatformsBlackLists["linux"]
1678 else: 1804 else:
1679 PlatformBlackLists = PlatformsBlackLists["mac"] 1805 PlatformBlackLists = PlatformsBlackLists["mac"]
1680 1806
1681 print("\nVersion Information") 1807 print("\nVersion Information")
1682 print("-------------------") 1808 print("-------------------")
1683 1809
1684 print("Python: {0:d}.{1:d}.{2:d}".format(*sys.version_info[:3])) 1810 print("Python: {0:d}.{1:d}.{2:d}".format(*sys.version_info[:3]))
1685 1811
1686 # check version of Qt 1812 # check version of Qt
1687 # =================== 1813 # ===================
1688 qtMajor = int(qVersion().split('.')[0]) 1814 qtMajor = int(qVersion().split(".")[0])
1689 qtMinor = int(qVersion().split('.')[1]) 1815 qtMinor = int(qVersion().split(".")[1])
1690 print("Qt6: {0}".format(qVersion().strip())) 1816 print("Qt6: {0}".format(qVersion().strip()))
1691 if qtMajor == 6 and qtMinor < 1: 1817 if qtMajor == 6 and qtMinor < 1:
1692 print('Sorry, you must have Qt version 6.1.0 or better.') 1818 print("Sorry, you must have Qt version 6.1.0 or better.")
1693 exit(2) 1819 exit(2)
1694 1820
1695 # check version of sip 1821 # check version of sip
1696 # ==================== 1822 # ====================
1697 with contextlib.suppress(ImportError, AttributeError): 1823 with contextlib.suppress(ImportError, AttributeError):
1698 try: 1824 try:
1699 from PyQt6 import sip 1825 from PyQt6 import sip
1700 except ImportError: 1826 except ImportError:
1701 import sip 1827 import sip
1702 print("sip:", sip.SIP_VERSION_STR.strip()) 1828 print("sip:", sip.SIP_VERSION_STR.strip())
1703 # always assume, that snapshots or dev versions are new enough 1829 # always assume, that snapshots or dev versions are new enough
1704 if ( 1830 if "snapshot" not in sip.SIP_VERSION_STR and "dev" not in sip.SIP_VERSION_STR:
1705 "snapshot" not in sip.SIP_VERSION_STR and
1706 "dev" not in sip.SIP_VERSION_STR
1707 ):
1708 if sip.SIP_VERSION < requiredVersions["sip"]: 1831 if sip.SIP_VERSION < requiredVersions["sip"]:
1709 print( 1832 print(
1710 'Sorry, you must have sip {0} or higher or' 1833 "Sorry, you must have sip {0} or higher or"
1711 ' a recent development release.' 1834 " a recent development release.".format(
1712 .format(versionToStr(requiredVersions["sip"])) 1835 versionToStr(requiredVersions["sip"])
1836 )
1713 ) 1837 )
1714 exit(3) 1838 exit(3)
1715 # check for blacklisted versions 1839 # check for blacklisted versions
1716 for vers in BlackLists["sip"] + PlatformBlackLists["sip"]: 1840 for vers in BlackLists["sip"] + PlatformBlackLists["sip"]:
1717 if vers == sip.SIP_VERSION: 1841 if vers == sip.SIP_VERSION:
1718 print( 1842 print(
1719 'Sorry, sip version {0} is not compatible with eric.' 1843 "Sorry, sip version {0} is not compatible with eric.".format(
1720 .format(versionToStr(vers))) 1844 versionToStr(vers)
1721 print('Please install another version.') 1845 )
1846 )
1847 print("Please install another version.")
1722 exit(3) 1848 exit(3)
1723 1849
1724 # check version of PyQt6 1850 # check version of PyQt6
1725 # ====================== 1851 # ======================
1726 from PyQt6.QtCore import PYQT_VERSION, PYQT_VERSION_STR 1852 from PyQt6.QtCore import PYQT_VERSION, PYQT_VERSION_STR
1853
1727 print("PyQt6:", PYQT_VERSION_STR.strip()) 1854 print("PyQt6:", PYQT_VERSION_STR.strip())
1728 # always assume, that snapshots or dev versions are new enough 1855 # always assume, that snapshots or dev versions are new enough
1729 if "snapshot" not in PYQT_VERSION_STR and "dev" not in PYQT_VERSION_STR: 1856 if "snapshot" not in PYQT_VERSION_STR and "dev" not in PYQT_VERSION_STR:
1730 if PYQT_VERSION < requiredVersions["pyqt6"]: 1857 if PYQT_VERSION < requiredVersions["pyqt6"]:
1731 print( 1858 print(
1732 'Sorry, you must have PyQt {0} or better or' 1859 "Sorry, you must have PyQt {0} or better or"
1733 ' a recent development release.' 1860 " a recent development release.".format(
1734 .format(versionToStr(requiredVersions["pyqt6"])) 1861 versionToStr(requiredVersions["pyqt6"])
1862 )
1735 ) 1863 )
1736 exit(4) 1864 exit(4)
1737 # check for blacklisted versions 1865 # check for blacklisted versions
1738 for vers in BlackLists["PyQt6"] + PlatformBlackLists["PyQt6"]: 1866 for vers in BlackLists["PyQt6"] + PlatformBlackLists["PyQt6"]:
1739 if vers == PYQT_VERSION: 1867 if vers == PYQT_VERSION:
1740 print('Sorry, PyQt version {0} is not compatible with eric.' 1868 print(
1741 .format(versionToStr(vers))) 1869 "Sorry, PyQt version {0} is not compatible with eric.".format(
1742 print('Please install another version.') 1870 versionToStr(vers)
1871 )
1872 )
1873 print("Please install another version.")
1743 exit(4) 1874 exit(4)
1744 1875
1745 # check version of QScintilla 1876 # check version of QScintilla
1746 # =========================== 1877 # ===========================
1747 from PyQt6.Qsci import QSCINTILLA_VERSION, QSCINTILLA_VERSION_STR 1878 from PyQt6.Qsci import QSCINTILLA_VERSION, QSCINTILLA_VERSION_STR
1879
1748 print("PyQt6-QScintilla:", QSCINTILLA_VERSION_STR.strip()) 1880 print("PyQt6-QScintilla:", QSCINTILLA_VERSION_STR.strip())
1749 # always assume, that snapshots or dev versions are new enough 1881 # always assume, that snapshots or dev versions are new enough
1750 if ( 1882 if "snapshot" not in QSCINTILLA_VERSION_STR and "dev" not in QSCINTILLA_VERSION_STR:
1751 "snapshot" not in QSCINTILLA_VERSION_STR and
1752 "dev" not in QSCINTILLA_VERSION_STR
1753 ):
1754 if QSCINTILLA_VERSION < requiredVersions["pyqt6-qscintilla"]: 1883 if QSCINTILLA_VERSION < requiredVersions["pyqt6-qscintilla"]:
1755 print( 1884 print(
1756 'Sorry, you must have PyQt6-QScintilla {0} or higher or' 1885 "Sorry, you must have PyQt6-QScintilla {0} or higher or"
1757 ' a recent development release.' 1886 " a recent development release.".format(
1758 .format(versionToStr(requiredVersions["pyqt6-qscintilla"])) 1887 versionToStr(requiredVersions["pyqt6-qscintilla"])
1888 )
1759 ) 1889 )
1760 exit(5) 1890 exit(5)
1761 # check for blacklisted versions 1891 # check for blacklisted versions
1762 for vers in ( 1892 for vers in BlackLists["QScintilla2"] + PlatformBlackLists["QScintilla2"]:
1763 BlackLists["QScintilla2"] +
1764 PlatformBlackLists["QScintilla2"]
1765 ):
1766 if vers == QSCINTILLA_VERSION: 1893 if vers == QSCINTILLA_VERSION:
1767 print( 1894 print(
1768 'Sorry, QScintilla2 version {0} is not compatible with' 1895 "Sorry, QScintilla2 version {0} is not compatible with"
1769 ' eric.'.format(versionToStr(vers))) 1896 " eric.".format(versionToStr(vers))
1770 print('Please install another version.') 1897 )
1898 print("Please install another version.")
1771 exit(5) 1899 exit(5)
1772 1900
1773 # print version info for additional modules 1901 # print version info for additional modules
1774 with contextlib.suppress(NameError, AttributeError): 1902 with contextlib.suppress(NameError, AttributeError):
1775 print("PyQt6-Charts:", QtCharts.PYQT_CHART_VERSION_STR) 1903 print("PyQt6-Charts:", QtCharts.PYQT_CHART_VERSION_STR)
1776 1904
1777 with contextlib.suppress(ImportError, AttributeError): 1905 with contextlib.suppress(ImportError, AttributeError):
1778 from PyQt6 import QtWebEngineCore 1906 from PyQt6 import QtWebEngineCore
1907
1779 print("PyQt6-WebEngine:", QtWebEngineCore.PYQT_WEBENGINE_VERSION_STR) 1908 print("PyQt6-WebEngine:", QtWebEngineCore.PYQT_WEBENGINE_VERSION_STR)
1780 1909
1781 print() 1910 print()
1782 print("All dependencies ok.") 1911 print("All dependencies ok.")
1783 print() 1912 print()
1784 1913
1785 1914
1786 def __pyName(py_dir, py_file): 1915 def __pyName(py_dir, py_file):
1787 """ 1916 """
1788 Local function to create the Python source file name for the compiled 1917 Local function to create the Python source file name for the compiled
1789 .ui file. 1918 .ui file.
1790 1919
1791 @param py_dir suggested name of the directory (string) 1920 @param py_dir suggested name of the directory (string)
1792 @param py_file suggested name for the compile source file (string) 1921 @param py_file suggested name for the compile source file (string)
1793 @return tuple of directory name (string) and source file name (string) 1922 @return tuple of directory name (string) and source file name (string)
1794 """ 1923 """
1795 return py_dir, "Ui_{0}".format(py_file) 1924 return py_dir, "Ui_{0}".format(py_file)
1798 def compileUiFiles(): 1927 def compileUiFiles():
1799 """ 1928 """
1800 Compile the .ui files to Python sources. 1929 Compile the .ui files to Python sources.
1801 """ 1930 """
1802 from PyQt6.uic import compileUiDir 1931 from PyQt6.uic import compileUiDir
1932
1803 compileUiDir(eric7SourceDir, True, __pyName) 1933 compileUiDir(eric7SourceDir, True, __pyName)
1804 1934
1805 1935
1806 def prepareInfoFile(fileName): 1936 def prepareInfoFile(fileName):
1807 """ 1937 """
1808 Function to prepare an Info.py file when installing from source. 1938 Function to prepare an Info.py file when installing from source.
1809 1939
1810 @param fileName name of the Python file containing the info (string) 1940 @param fileName name of the Python file containing the info (string)
1811 """ 1941 """
1812 if not fileName: 1942 if not fileName:
1813 return 1943 return
1814 1944
1815 with contextlib.suppress(OSError): 1945 with contextlib.suppress(OSError):
1816 os.rename(fileName, fileName + ".orig") 1946 os.rename(fileName, fileName + ".orig")
1817 localHg = ( 1947 localHg = (
1818 os.path.join(sys.exec_prefix, "Scripts", "hg.exe") 1948 os.path.join(sys.exec_prefix, "Scripts", "hg.exe")
1819 if sys.platform.startswith(("win", "cygwin")) else 1949 if sys.platform.startswith(("win", "cygwin"))
1820 os.path.join(sys.exec_prefix, "bin", "hg") 1950 else os.path.join(sys.exec_prefix, "bin", "hg")
1821 ) 1951 )
1822 for hg in (localHg, "hg"): 1952 for hg in (localHg, "hg"):
1823 with contextlib.suppress(OSError, subprocess.CalledProcessError): 1953 with contextlib.suppress(OSError, subprocess.CalledProcessError):
1824 hgOut = subprocess.run( # secok 1954 hgOut = subprocess.run( # secok
1825 [hg, "identify", "-i"], check=True, 1955 [hg, "identify", "-i"], check=True, capture_output=True, text=True
1826 capture_output=True, text=True
1827 ).stdout 1956 ).stdout
1828 if hgOut: 1957 if hgOut:
1829 break 1958 break
1830 else: 1959 else:
1831 hgOut = "" 1960 hgOut = ""
1833 hgOut = hgOut.strip() 1962 hgOut = hgOut.strip()
1834 if hgOut.endswith("+"): 1963 if hgOut.endswith("+"):
1835 hgOut = hgOut[:-1] 1964 hgOut = hgOut[:-1]
1836 with open(fileName + ".orig", "r", encoding="utf-8") as f: 1965 with open(fileName + ".orig", "r", encoding="utf-8") as f:
1837 text = f.read() 1966 text = f.read()
1838 text = ( 1967 text = text.replace("@@REVISION@@", hgOut).replace(
1839 text.replace("@@REVISION@@", hgOut) 1968 "@@VERSION@@", "rev_" + hgOut
1840 .replace("@@VERSION@@", "rev_" + hgOut)
1841 ) 1969 )
1842 copyToFile(fileName, text) 1970 copyToFile(fileName, text)
1843 else: 1971 else:
1844 shutil.copy(fileName + ".orig", fileName) 1972 shutil.copy(fileName + ".orig", fileName)
1845 1973
1846 1974
1847 def getWinregEntry(name, path): 1975 def getWinregEntry(name, path):
1848 """ 1976 """
1849 Function to get an entry from the Windows Registry. 1977 Function to get an entry from the Windows Registry.
1850 1978
1851 @param name variable name 1979 @param name variable name
1852 @type str 1980 @type str
1853 @param path registry path of the variable 1981 @param path registry path of the variable
1854 @type str 1982 @type str
1855 @return value of requested registry variable 1983 @return value of requested registry variable
1857 """ 1985 """
1858 try: 1986 try:
1859 import winreg 1987 import winreg
1860 except ImportError: 1988 except ImportError:
1861 return None 1989 return None
1862 1990
1863 try: 1991 try:
1864 registryKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, 1992 registryKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, path, 0, winreg.KEY_READ)
1865 winreg.KEY_READ)
1866 value, _ = winreg.QueryValueEx(registryKey, name) 1993 value, _ = winreg.QueryValueEx(registryKey, name)
1867 winreg.CloseKey(registryKey) 1994 winreg.CloseKey(registryKey)
1868 return value 1995 return value
1869 except WindowsError: 1996 except WindowsError:
1870 return None 1997 return None
1871 1998
1872 1999
1873 def createWindowsShortcut(linkPath, targetPath, iconPath): 2000 def createWindowsShortcut(linkPath, targetPath, iconPath):
1874 """ 2001 """
1875 Create Windows shortcut. 2002 Create Windows shortcut.
1876 2003
1877 @param linkPath path of the shortcut file 2004 @param linkPath path of the shortcut file
1878 @type str 2005 @type str
1879 @param targetPath path the shortcut shall point to 2006 @param targetPath path the shortcut shall point to
1880 @type str 2007 @type str
1881 @param iconPath path of the icon file 2008 @param iconPath path of the icon file
1882 @type str 2009 @type str
1883 """ 2010 """
1884 from win32com.client import Dispatch 2011 from win32com.client import Dispatch
1885 from pywintypes import com_error 2012 from pywintypes import com_error
1886 2013
1887 with contextlib.suppress(com_error): 2014 with contextlib.suppress(com_error):
1888 shell = Dispatch('WScript.Shell') 2015 shell = Dispatch("WScript.Shell")
1889 shortcut = shell.CreateShortCut(linkPath) 2016 shortcut = shell.CreateShortCut(linkPath)
1890 shortcut.Targetpath = targetPath 2017 shortcut.Targetpath = targetPath
1891 shortcut.WorkingDirectory = os.path.dirname(targetPath) 2018 shortcut.WorkingDirectory = os.path.dirname(targetPath)
1892 shortcut.IconLocation = iconPath 2019 shortcut.IconLocation = iconPath
1893 shortcut.save() 2020 shortcut.save()
1894 2021
1895 2022
1896 def windowsDesktopNames(): 2023 def windowsDesktopNames():
1897 """ 2024 """
1898 Function to generate the link names for the Windows Desktop. 2025 Function to generate the link names for the Windows Desktop.
1899 2026
1900 @return list of desktop link names 2027 @return list of desktop link names
1901 @rtype list of str 2028 @rtype list of str
1902 """ 2029 """
1903 return [e[0] for e in windowsDesktopEntries()] 2030 return [e[0] for e in windowsDesktopEntries()]
1904 2031
1905 2032
1906 def windowsDesktopEntries(): 2033 def windowsDesktopEntries():
1907 """ 2034 """
1908 Function to generate data for the Windows Desktop links. 2035 Function to generate data for the Windows Desktop links.
1909 2036
1910 @return list of tuples containing the desktop link name, 2037 @return list of tuples containing the desktop link name,
1911 the link target and the icon target 2038 the link target and the icon target
1912 @rtype list of tuples of (str, str, str) 2039 @rtype list of tuples of (str, str, str)
1913 """ 2040 """
1914 global cfg 2041 global cfg
1915 2042
1916 majorVersion, minorVersion = sys.version_info[:2] 2043 majorVersion, minorVersion = sys.version_info[:2]
1917 entriesTemplates = [ 2044 entriesTemplates = [
1918 ("eric7 (Python {0}.{1}).lnk", 2045 (
1919 os.path.join(cfg["bindir"], "eric7.cmd"), 2046 "eric7 (Python {0}.{1}).lnk",
1920 os.path.join(cfg["ericPixDir"], "eric7.ico")), 2047 os.path.join(cfg["bindir"], "eric7.cmd"),
1921 ("eric7 Browser (Python {0}.{1}).lnk", 2048 os.path.join(cfg["ericPixDir"], "eric7.ico"),
1922 os.path.join(cfg["bindir"], "eric7_browser.cmd"), 2049 ),
1923 os.path.join(cfg["ericPixDir"], "ericWeb48.ico")), 2050 (
2051 "eric7 Browser (Python {0}.{1}).lnk",
2052 os.path.join(cfg["bindir"], "eric7_browser.cmd"),
2053 os.path.join(cfg["ericPixDir"], "ericWeb48.ico"),
2054 ),
1924 ] 2055 ]
1925 2056
1926 return [ 2057 return [
1927 (e[0].format(majorVersion, minorVersion), e[1], e[2]) 2058 (e[0].format(majorVersion, minorVersion), e[1], e[2]) for e in entriesTemplates
1928 for e in entriesTemplates
1929 ] 2059 ]
1930 2060
1931 2061
1932 def windowsProgramsEntry(): 2062 def windowsProgramsEntry():
1933 """ 2063 """
1934 Function to generate the name of the Start Menu top entry. 2064 Function to generate the name of the Start Menu top entry.
1935 2065
1936 @return name of the Start Menu top entry 2066 @return name of the Start Menu top entry
1937 @rtype str 2067 @rtype str
1938 """ 2068 """
1939 majorVersion, minorVersion = sys.version_info[:2] 2069 majorVersion, minorVersion = sys.version_info[:2]
1940 return "eric7 (Python {0}.{1})".format(majorVersion, minorVersion) 2070 return "eric7 (Python {0}.{1})".format(majorVersion, minorVersion)
1955 global macAppBundlePath, macAppBundleName, macPythonExe 2085 global macAppBundlePath, macAppBundleName, macPythonExe
1956 global installApis, doCleanDesktopLinks, yes2All 2086 global installApis, doCleanDesktopLinks, yes2All
1957 global createInstallInfoFile, installCwd 2087 global createInstallInfoFile, installCwd
1958 global ignorePyqt6Tools 2088 global ignorePyqt6Tools
1959 global verbose 2089 global verbose
1960 2090
1961 if sys.version_info < (3, 7, 0) or sys.version_info > (3, 99, 99): 2091 if sys.version_info < (3, 7, 0) or sys.version_info > (3, 99, 99):
1962 print('Sorry, eric requires at least Python 3.7 for running.') 2092 print("Sorry, eric requires at least Python 3.7 for running.")
1963 exit(5) 2093 exit(5)
1964 2094
1965 progName = os.path.basename(argv[0]) 2095 progName = os.path.basename(argv[0])
1966 2096
1967 installCwd = os.getcwd() 2097 installCwd = os.getcwd()
1968 2098
1969 if os.path.dirname(argv[0]): 2099 if os.path.dirname(argv[0]):
1970 os.chdir(os.path.dirname(argv[0])) 2100 os.chdir(os.path.dirname(argv[0]))
1971 2101
1972 initGlobals() 2102 initGlobals()
1973 2103
1974 try: 2104 try:
1975 if sys.platform.startswith(("win", "cygwin")): 2105 if sys.platform.startswith(("win", "cygwin")):
1976 optlist, args = getopt.getopt( 2106 optlist, args = getopt.getopt(
1977 argv[1:], "chvxza:b:d:f:", 2107 argv[1:],
1978 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"]) 2108 "chvxza:b:d:f:",
2109 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"],
2110 )
1979 elif sys.platform == "darwin": 2111 elif sys.platform == "darwin":
1980 optlist, args = getopt.getopt( 2112 optlist, args = getopt.getopt(
1981 argv[1:], "chvxza:b:d:f:i:m:n:p:", 2113 argv[1:],
1982 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"]) 2114 "chvxza:b:d:f:i:m:n:p:",
2115 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"],
2116 )
1983 else: 2117 else:
1984 optlist, args = getopt.getopt( 2118 optlist, args = getopt.getopt(
1985 argv[1:], "chvxza:b:d:f:i:", 2119 argv[1:],
1986 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"]) 2120 "chvxza:b:d:f:i:",
2121 ["help", "no-apis", "no-info", "no-tools", "verbose", "yes"],
2122 )
1987 except getopt.GetoptError as err: 2123 except getopt.GetoptError as err:
1988 print(err) 2124 print(err)
1989 usage() 2125 usage()
1990 2126
1991 global platBinDir 2127 global platBinDir
1992 2128
1993 depChecks = True 2129 depChecks = True
1994 2130
1995 for opt, arg in optlist: 2131 for opt, arg in optlist:
1996 if opt in ["-h", "--help"]: 2132 if opt in ["-h", "--help"]:
1997 usage(0) 2133 usage(0)
2010 elif opt == "-z": 2146 elif opt == "-z":
2011 doCompile = False 2147 doCompile = False
2012 elif opt == "-f": 2148 elif opt == "-f":
2013 with open(arg) as f: 2149 with open(arg) as f:
2014 try: 2150 try:
2015 exec(compile(f.read(), arg, 'exec'), globals()) 2151 exec(compile(f.read(), arg, "exec"), globals())
2016 # secok 2152 # secok
2017 if len(cfg) != configLength: 2153 if len(cfg) != configLength:
2018 print("The configuration dictionary in '{0}' is" 2154 print(
2019 " incorrect. Aborting".format(arg)) 2155 "The configuration dictionary in '{0}' is"
2156 " incorrect. Aborting".format(arg)
2157 )
2020 exit(6) 2158 exit(6)
2021 except Exception: 2159 except Exception:
2022 cfg = {} 2160 cfg = {}
2023 elif opt == "-m": 2161 elif opt == "-m":
2024 macAppBundleName = arg 2162 macAppBundleName = arg
2036 ignorePyqt6Tools = True 2174 ignorePyqt6Tools = True
2037 elif opt == "--no-info": 2175 elif opt == "--no-info":
2038 createInstallInfoFile = False 2176 createInstallInfoFile = False
2039 elif opt in ["-v", "--verbose"]: 2177 elif opt in ["-v", "--verbose"]:
2040 verbose = True 2178 verbose = True
2041 2179
2042 infoName = "" 2180 infoName = ""
2043 installFromSource = not os.path.isdir(sourceDir) 2181 installFromSource = not os.path.isdir(sourceDir)
2044 2182
2045 # check dependencies 2183 # check dependencies
2046 if depChecks: 2184 if depChecks:
2047 doDependancyChecks() 2185 doDependancyChecks()
2048 2186
2049 if installFromSource: 2187 if installFromSource:
2050 sourceDir = os.path.abspath("..") 2188 sourceDir = os.path.abspath("..")
2051 2189
2052 eric7SourceDir = ( 2190 eric7SourceDir = (
2053 os.path.join(sourceDir, "eric7") 2191 os.path.join(sourceDir, "eric7")
2054 if os.path.exists(os.path.join(sourceDir, "eric7")) else 2192 if os.path.exists(os.path.join(sourceDir, "eric7"))
2055 os.path.join(sourceDir, "src", "eric7") 2193 else os.path.join(sourceDir, "src", "eric7")
2056 ) 2194 )
2057 2195
2058 # cleanup source if installing from source 2196 # cleanup source if installing from source
2059 if installFromSource: 2197 if installFromSource:
2060 print("Cleaning up source ...") 2198 print("Cleaning up source ...")
2061 cleanupSource(eric7SourceDir) 2199 cleanupSource(eric7SourceDir)
2062 print() 2200 print()
2063 2201
2064 configName = os.path.join(eric7SourceDir, "eric7config.py") 2202 configName = os.path.join(eric7SourceDir, "eric7config.py")
2065 if os.path.exists(os.path.join(sourceDir, ".hg")): 2203 if os.path.exists(os.path.join(sourceDir, ".hg")):
2066 # we are installing from source with repo 2204 # we are installing from source with repo
2067 infoName = os.path.join(eric7SourceDir, "UI", "Info.py") 2205 infoName = os.path.join(eric7SourceDir, "UI", "Info.py")
2068 prepareInfoFile(infoName) 2206 prepareInfoFile(infoName)
2069 2207
2070 if len(cfg) == 0: 2208 if len(cfg) == 0:
2071 createInstallConfig() 2209 createInstallConfig()
2072 2210
2073 # get rid of development config file, if it exists 2211 # get rid of development config file, if it exists
2074 with contextlib.suppress(OSError): 2212 with contextlib.suppress(OSError):
2075 if installFromSource: 2213 if installFromSource:
2076 os.rename(configName, configName + ".orig") 2214 os.rename(configName, configName + ".orig")
2077 configNameC = configName + 'c' 2215 configNameC = configName + "c"
2078 if os.path.exists(configNameC): 2216 if os.path.exists(configNameC):
2079 os.remove(configNameC) 2217 os.remove(configNameC)
2080 os.remove(configName) 2218 os.remove(configName)
2081 2219
2082 # cleanup old installation 2220 # cleanup old installation
2083 print("Cleaning up old installation ...") 2221 print("Cleaning up old installation ...")
2084 try: 2222 try:
2085 if doCleanup: 2223 if doCleanup:
2086 if distDir: 2224 if distDir:
2087 shutil.rmtree(distDir, True) 2225 shutil.rmtree(distDir, True)
2088 else: 2226 else:
2089 cleanUp() 2227 cleanUp()
2090 except OSError as msg: 2228 except OSError as msg:
2091 sys.stderr.write('Error: {0}\nTry install as root.\n'.format(msg)) 2229 sys.stderr.write("Error: {0}\nTry install as root.\n".format(msg))
2092 exit(7) 2230 exit(7)
2093 2231
2094 # Create a config file and delete the default one 2232 # Create a config file and delete the default one
2095 print("\nCreating configuration file ...") 2233 print("\nCreating configuration file ...")
2096 createConfig() 2234 createConfig()
2097 2235
2098 createInstallInfo() 2236 createInstallInfo()
2099 2237
2100 # Compile .ui files 2238 # Compile .ui files
2101 print("\nCompiling user interface files ...") 2239 print("\nCompiling user interface files ...")
2102 # step 1: remove old Ui_*.py files 2240 # step 1: remove old Ui_*.py files
2103 for root, _, files in os.walk(sourceDir): 2241 for root, _, files in os.walk(sourceDir):
2104 for file in [f for f in files if fnmatch.fnmatch(f, 'Ui_*.py')]: 2242 for file in [f for f in files if fnmatch.fnmatch(f, "Ui_*.py")]:
2105 os.remove(os.path.join(root, file)) 2243 os.remove(os.path.join(root, file))
2106 # step 2: compile the forms 2244 # step 2: compile the forms
2107 compileUiFiles() 2245 compileUiFiles()
2108 2246
2109 if doCompile: 2247 if doCompile:
2110 print("\nCompiling source files ...") 2248 print("\nCompiling source files ...")
2111 skipRe = re.compile(r"DebugClients[\\/]Python[\\/]") 2249 skipRe = re.compile(r"DebugClients[\\/]Python[\\/]")
2112 sys.stdout = io.StringIO() 2250 sys.stdout = io.StringIO()
2113 if distDir: 2251 if distDir:
2114 compileall.compile_dir( 2252 compileall.compile_dir(
2115 eric7SourceDir, 2253 eric7SourceDir,
2116 ddir=os.path.join(distDir, modDir, cfg['ericDir']), 2254 ddir=os.path.join(distDir, modDir, cfg["ericDir"]),
2117 rx=skipRe, 2255 rx=skipRe,
2118 quiet=True) 2256 quiet=True,
2257 )
2119 py_compile.compile( 2258 py_compile.compile(
2120 configName, 2259 configName, dfile=os.path.join(distDir, modDir, "eric7config.py")
2121 dfile=os.path.join(distDir, modDir, "eric7config.py")) 2260 )
2122 else: 2261 else:
2123 compileall.compile_dir( 2262 compileall.compile_dir(
2124 eric7SourceDir, 2263 eric7SourceDir,
2125 ddir=os.path.join(modDir, cfg['ericDir']), 2264 ddir=os.path.join(modDir, cfg["ericDir"]),
2126 rx=skipRe, 2265 rx=skipRe,
2127 quiet=True) 2266 quiet=True,
2128 py_compile.compile(configName, 2267 )
2129 dfile=os.path.join(modDir, "eric7config.py")) 2268 py_compile.compile(configName, dfile=os.path.join(modDir, "eric7config.py"))
2130 sys.stdout = sys.__stdout__ 2269 sys.stdout = sys.__stdout__
2131 print("\nInstalling eric ...") 2270 print("\nInstalling eric ...")
2132 res = installEric() 2271 res = installEric()
2133 2272
2134 if createInstallInfoFile: 2273 if createInstallInfoFile:
2135 with open(os.path.join(cfg["ericDir"], 2274 with open(
2136 installInfoName), "w") as installInfoFile: 2275 os.path.join(cfg["ericDir"], installInfoName), "w"
2276 ) as installInfoFile:
2137 json.dump(installInfo, installInfoFile, indent=2) 2277 json.dump(installInfo, installInfoFile, indent=2)
2138 2278
2139 # do some cleanup 2279 # do some cleanup
2140 with contextlib.suppress(OSError): 2280 with contextlib.suppress(OSError):
2141 if installFromSource: 2281 if installFromSource:
2142 os.remove(configName) 2282 os.remove(configName)
2143 configNameC = configName + 'c' 2283 configNameC = configName + "c"
2144 if os.path.exists(configNameC): 2284 if os.path.exists(configNameC):
2145 os.remove(configNameC) 2285 os.remove(configNameC)
2146 os.rename(configName + ".orig", configName) 2286 os.rename(configName + ".orig", configName)
2147 with contextlib.suppress(OSError): 2287 with contextlib.suppress(OSError):
2148 if installFromSource and infoName: 2288 if installFromSource and infoName:
2149 os.remove(infoName) 2289 os.remove(infoName)
2150 infoNameC = infoName + 'c' 2290 infoNameC = infoName + "c"
2151 if os.path.exists(infoNameC): 2291 if os.path.exists(infoNameC):
2152 os.remove(infoNameC) 2292 os.remove(infoNameC)
2153 os.rename(infoName + ".orig", infoName) 2293 os.rename(infoName + ".orig", infoName)
2154 2294
2155 print("\nInstallation complete.") 2295 print("\nInstallation complete.")
2156 print() 2296 print()
2157 2297
2158 exit(res) 2298 exit(res)
2159 2299
2160 2300
2161 if __name__ == "__main__": 2301 if __name__ == "__main__":
2162 try: 2302 try:
2163 main(sys.argv) 2303 main(sys.argv)
2164 except SystemExit: 2304 except SystemExit:
2165 raise 2305 raise
2166 except Exception: 2306 except Exception:
2167 print("""An internal error occured. Please report all the output""" 2307 print(
2168 """ of the program,\nincluding the following traceback, to""" 2308 """An internal error occured. Please report all the output"""
2169 """ eric-bugs@eric-ide.python-projects.org.\n""") 2309 """ of the program,\nincluding the following traceback, to"""
2310 """ eric-bugs@eric-ide.python-projects.org.\n"""
2311 )
2170 raise 2312 raise
2171 2313
2172 # 2314 #
2173 # eflag: noqa = M801 2315 # eflag: noqa = M801

eric ide

mercurial