Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Helpers, inspired from `utils.py <https://github.com/winpython/winpython/blob/master/winpython/utils.py>`_ 

5""" 

6from __future__ import print_function 

7 

8import os 

9import os.path as osp 

10import subprocess 

11import re 

12import sys 

13import locale 

14 

15 

16# ============================================================================= 

17# Patch chebang line (courtesy of Christoph Gohlke) 

18# ============================================================================= 

19def patch_shebang_line(fname, pad=b' ', fLOG=print): 

20 """ 

21 Remove absolute path to python.exe in shebang lines. 

22 

23 @param python python extractor 

24 @param pad pad 

25 @param fLOG logging function 

26 @return boolean, True if patched, False otherwise 

27 """ 

28 if sys.version_info[0] == 2: 

29 shebang_line = re.compile(r"(#!.+pythonw?\\.exe)") # Python2.7 

30 else: 

31 shebang_line = re.compile(b"(#!.+pythonw?\\.exe)") # Python3+ 

32 

33 with open(fname, 'rb') as fh: 

34 content = fh.read() 

35 

36 content = shebang_line.split(content, maxsplit=1) 

37 if len(content) != 3: 

38 return 

39 exe = os.path.basename(content[1][2:]) 

40 content[1] = b'#!' + exe + (pad * (len(content[1]) - len(exe) - 2)) 

41 content = b''.join(content) 

42 

43 try: 

44 with open(fname, 'wb') as fh: 

45 fh.write(content) 

46 fLOG("[pymy] patched", fname) 

47 return True 

48 except Exception: 

49 fLOG("[pymy] failed to patch", fname) 

50 return False 

51 

52 

53def get_env(name, current=True): 

54 """ 

55 Return HKCU/HKLM environment variable name and value 

56 

57 @param name name to look for 

58 @param current switch between *HKEY_CURRENT_USER* (True) and *HKEY_LOCAL_MACHINE* (False) 

59 @return tuple (see below) 

60 

61 For example, get_user_env('PATH') may returns:: 

62 

63 ('Path', u'C:\\Program Files\\Intel\\WiFi\\bin\\') 

64 """ 

65 import winreg 

66 root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE 

67 key = winreg.OpenKey(root, "Environment") 

68 for index in range(0, winreg.QueryInfoKey(key)[1]): 

69 try: 

70 value = winreg.EnumValue(key, index) 

71 if value[0].lower() == name.lower(): 

72 # Return both value[0] and value[1] because value[0] could be 

73 # different from name (lowercase/uppercase) 

74 return value[0], value[1] 

75 except Exception: 

76 break 

77 

78 

79def set_env(name, value, current=True): 

80 """ 

81 Set HKCU/HKLM environment variables 

82 

83 

84 @param name name to look for 

85 @param current switch between *HKEY_CURRENT_USER* (True) and *HKEY_LOCAL_MACHINE* (False) 

86 """ 

87 import winreg 

88 root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE 

89 key = winreg.OpenKey(root, "Environment") 

90 try: 

91 _x, key_type = winreg.QueryValueEx(key, name) 

92 except WindowsError: 

93 key_type = winreg.REG_EXPAND_SZ 

94 key = winreg.OpenKey(root, "Environment", 0, winreg.KEY_SET_VALUE) 

95 winreg.SetValueEx(key, name, 0, key_type, value) 

96 from win32gui import SendMessageTimeout 

97 from win32con import (HWND_BROADCAST, WM_SETTINGCHANGE, 

98 SMTO_ABORTIFHUNG) 

99 SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 

100 "Environment", SMTO_ABORTIFHUNG, 5000) 

101 

102 

103def create_shortcut(path, description, filename, 

104 arguments="", workdir="", iconpath="", iconindex=0): 

105 """ 

106 Create Windows shortcut (.lnk file) 

107 

108 @param path where to store the link 

109 @param description description 

110 @param filename link name 

111 @param arguments arguments to store 

112 @param workdir working directory 

113 @para iconpath icon 

114 @param iconindex icon index 

115 @return filename 

116 """ 

117 import pythoncom 

118 from win32com.shell import shell 

119 ilink = pythoncom.CoCreateInstance(shell.CLSID_ShellLink, None, 

120 pythoncom.CLSCTX_INPROC_SERVER, 

121 shell.IID_IShellLink) 

122 ilink.SetPath(path) 

123 ilink.SetDescription(description) 

124 if arguments: 

125 ilink.SetArguments(arguments) 

126 if workdir: 

127 ilink.SetWorkingDirectory(workdir) 

128 if iconpath or iconindex: 

129 ilink.SetIconLocation(iconpath, iconindex) 

130 # now save it. 

131 ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile) 

132 if not filename.endswith('.lnk'): 

133 filename += '.lnk' 

134 filename = os.path.join(path, filename) 

135 ipf.Save(filename, 0) 

136 return filename 

137 

138 

139def decode_fs_string(string): 

140 """Convert string from file system charset to unicode""" 

141 charset = sys.getfilesystemencoding() 

142 if charset is None: 

143 charset = locale.getpreferredencoding() 

144 return string.decode(charset) 

145 

146 

147def exec_shell_cmd(args, path): 

148 """Execute shell command (*args* is a list of arguments) in *path*""" 

149 # print " ".join(args) 

150 process = subprocess.Popen(args, stdout=subprocess.PIPE, 

151 stderr=subprocess.PIPE, cwd=path, shell=True) 

152 return decode_fs_string(process.stdout.read()) 

153 

154 

155def get_gcc_version(path): 

156 """Return version of the GCC compiler installed in *path*""" 

157 return exec_shell_cmd('gcc --version', path).splitlines()[0].split()[-1] 

158 

159 

160def get_r_version(path): 

161 """Return version of the R installed in *path*""" 

162 return exec_shell_cmd('dir ..\\README.R*', path).splitlines()[-3].split("-")[-1] 

163 

164 

165def get_julia_version(path): 

166 """Return version of the Julia installed in *path*""" 

167 return exec_shell_cmd('julia.exe -v', path).splitlines()[0].split(" ")[-1] 

168 

169 

170def python_query(cmd, path): 

171 """Execute Python command using the Python interpreter located in *path*""" 

172 res = exec_shell_cmd('python -c "%s"' % cmd, path).splitlines() 

173 if not res: 

174 raise Exception( 

175 "CMD:\n{0}\nRES:\n{1}\nPATH:\n{2}".format(cmd, res, path)) 

176 return res[0] 

177 

178 

179def get_python_infos(path): 

180 """Return (version, architecture) for the Python distribution located in 

181 *path*. The version number is limited to MAJOR.MINOR, the architecture is 

182 an integer: 32 or 64""" 

183 is_64 = python_query('import sys; print(sys.maxsize > 2**32)', path) 

184 arch = {'True': 64, 'False': 32}.get(is_64, None) 

185 ver = python_query("import sys; print('%d.%d' % (sys.version_info.major, " 

186 "sys.version_info.minor))", path) 

187 if re.match(r'([0-9]*)\.([0-9]*)', ver) is None: 

188 ver = None 

189 return ver, arch 

190 

191 

192def get_python_long_version(path): 

193 """Return long version (X.Y.Z) for the Python distribution located in 

194 *path*""" 

195 ver = python_query("import sys; print('%d.%d.%d' % " 

196 "(sys.version_info.major, sys.version_info.minor," 

197 "sys.version_info.micro))", path) 

198 if re.match(r'([0-9]*)\.([0-9]*)\.([0-9]*)', ver) is None: 

199 ver = None 

200 return ver 

201 

202 

203# ============================================================================= 

204# Patch sourcefile (instead of forking packages) 

205# ============================================================================= 

206def patch_sourcefile(fname, in_text, out_text, silent_mode=False): 

207 """Replace a string in a source file""" 

208 import io 

209 if osp.isfile(fname) and not in_text == out_text: 

210 with io.open(fname, 'r') as fh: 

211 content = fh.read() 

212 new_content = content.replace(in_text, out_text) 

213 if not new_content == content: 

214 if not silent_mode: 

215 print("patching ", fname, "from", in_text, "to", out_text) 

216 with io.open(fname, 'wt') as fh: 

217 fh.write(new_content) 

218 

219# ============================================================================= 

220# Patch sourcelines (instead of forking packages) 

221# ============================================================================= 

222 

223 

224def patch_sourcelines(fname, in_line_start, out_line, endline='\n', silent_mode=False): 

225 """Replace the middle of lines between in_line_start and endline """ 

226 import io 

227 import os.path as osp 

228 if osp.isfile(fname): 

229 with io.open(fname, 'r') as fh: 

230 contents = fh.readlines() 

231 content = "".join(contents) 

232 for l in range(len(contents)): 

233 if contents[l].startswith(in_line_start): 

234 begining, middle = in_line_start, contents[ 

235 l][len(in_line_start):] 

236 ending = "" 

237 if middle.find(endline) > 0: 

238 ending = endline + \ 

239 endline.join(middle.split(endline)[1:]) 

240 middle = middle.split(endline)[0] 

241 middle = out_line 

242 new_line = begining + middle + ending 

243 if not new_line == contents[l]: 

244 if not silent_mode: 

245 print( 

246 "patching ", fname, " from\n", contents[l], "\nto\n", new_line) 

247 contents[l] = new_line 

248 new_content = "".join(contents) 

249 if not new_content == content: 

250 # if not silent_mode: 

251 # print("patching ", fname, "from", content, "to", new_content) 

252 with io.open(fname, 'wt') as fh: 

253 try: 

254 fh.write(new_content) 

255 except Exception as e: 

256 print("impossible to patch", fname, "from", content, 

257 "to", new_content, " --- ", str(e).replace("\n", "--")) 

258 

259 

260WININST_PATTERN = (r'([a-zA-Z0-9\-\_]*|[a-zA-Z\-\_\.]*)-([0-9\.\-]*[a-z]*[0-9]?)(-Qt-([0-9\.]+))?.(win32|win\-amd64)' + 

261 r'(-py([0-9\.]+))?(-setup)?\.exe') 

262 

263# SOURCE_PATTERN defines what an acceptable source package name is 

264# As of 2014-09-08 : 

265# - the wheel package format is accepte in source directory 

266# - the tricky regexp is tuned also to support the odd jolib naming : 

267# . joblib-0.8.3_r1-py2.py3-none-any.whl, 

268# . joblib-0.8.3-r1.tar.gz 

269 

270SOURCE_PATTERN = r'([a-zA-Z0-9\-\_\.]*)-([0-9\.\_]*[a-z]*[0-9]?)(\.zip|\.tar\.gz|\-(py[2-7]*|py[2-7]*\.py[2-7]*)\-none\-any\.whl)' 

271 

272# WHEELBIN_PATTERN defines what an acceptable binary wheel package is 

273# "cp([0-9]*)" to replace per cp(34) for python3.4 

274# "win32|win\_amd64" to replace per "win\_amd64" for 64bit 

275WHEELBIN_PATTERN = r'([a-zA-Z0-9\-\_\.]*)-([0-9\.\_]*[a-z0-9\+]*[0-9]?)-cp([0-9]*)\-none\-(win32|win\_amd64)\.whl' 

276 

277 

278def get_source_package_infos(fname): 

279 """Return a tuple (name, version) of the Python source package""" 

280 match = re.match(SOURCE_PATTERN, osp.basename(fname)) 

281 if match is not None: 

282 return match.groups()[:2] 

283 

284 

285def do_script(this_script, python_exe=None, 

286 verbose=False, install_options=None): 

287 """Execute a script (get-pip typically)""" 

288 if python_exe is None: 

289 python_exe = sys.executable 

290 assert osp.isfile(python_exe) 

291 myroot = os.path.dirname(python_exe) 

292 

293 # cmd = [python_exe, myroot + r'\Scripts\pip-script.py', 'install'] 

294 cmd = [python_exe] 

295 if install_options: 

296 cmd += install_options # typically ['--no-deps'] 

297 print('script install_options', install_options) 

298 cmd += [this_script] 

299 # print('build_wheel', myroot, cmd) 

300 print("Executing ", cmd) 

301 

302 if verbose: 

303 subprocess.call(cmd, cwd=myroot) 

304 else: 

305 p = subprocess.Popen(cmd, cwd=myroot, stdout=subprocess.PIPE, 

306 stderr=subprocess.PIPE) 

307 p.communicate() 

308 p.stdout.close() 

309 p.stderr.close() 

310 if verbose: 

311 print("Executed " % cmd) 

312 return 'ok' 

313 

314 

315KEY_C = r"Software\Classes\%s" 

316KEY_C0 = KEY_C % r"Python.%sFile\shell" 

317KEY_C1 = KEY_C % r"Python.%sFile\shell\%s" 

318KEY_C2 = KEY_C1 + r"\command" 

319KEY_DROP0 = KEY_C % r"Python.%sFile\shellex" 

320KEY_DROP1 = KEY_C % r"Python.%sFile\shellex\DropHandler" 

321KEY_I = KEY_C % r"Python.%sFile\DefaultIcon" 

322KEY_D = KEY_C % r"Python.%sFile" 

323EWI = "Edit with IDLE" 

324EWS = "Edit with Spyder" 

325 

326KEY_S = r"Software\Python" 

327KEY_S0 = KEY_S + r"\PythonCore" 

328KEY_S1 = KEY_S0 + r"\%s" 

329 

330 

331def register(target, current=True): 

332 """Register a Python distribution in Windows registry""" 

333 import winreg 

334 root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE 

335 

336 # Extensions 

337 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".py"), 

338 "", 0, winreg.REG_SZ, "Python.File") 

339 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".pyw"), 

340 "", 0, winreg.REG_SZ, "Python.NoConFile") 

341 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".pyc"), 

342 "", 0, winreg.REG_SZ, "Python.CompiledFile") 

343 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".pyo"), 

344 "", 0, winreg.REG_SZ, "Python.CompiledFile") 

345 

346 # MIME types 

347 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".py"), 

348 "Content Type", 0, winreg.REG_SZ, "text/plain") 

349 winreg.SetValueEx(winreg.CreateKey(root, KEY_C % ".pyw"), 

350 "Content Type", 0, winreg.REG_SZ, "text/plain") 

351 

352 # Verbs 

353 python = osp.abspath(osp.join(target, 'python.exe')) 

354 pythonw = osp.abspath(osp.join(target, 'pythonw.exe')) 

355 spyder = osp.abspath(osp.join(target, os.pardir, 'Spyder.exe')) 

356 if not osp.isfile(spyder): 

357 spyder = '%s" "%s\\Scripts\\spyder' % (pythonw, target) 

358 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("", "open")), 

359 "", 0, winreg.REG_SZ, '"%s" "%%1" %%*' % python) 

360 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("NoCon", "open")), 

361 "", 0, winreg.REG_SZ, '"%s" "%%1" %%*' % pythonw) 

362 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("Compiled", "open")), 

363 "", 0, winreg.REG_SZ, '"%s" "%%1" %%*' % python) 

364 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("", EWI)), 

365 "", 0, winreg.REG_SZ, 

366 '"%s" "%s\\Lib\\idlelib\\idle.pyw" -n -e "%%1"' 

367 % (pythonw, target)) 

368 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("NoCon", EWI)), 

369 "", 0, winreg.REG_SZ, 

370 '"%s" "%s\\Lib\\idlelib\\idle.pyw" -n -e "%%1"' 

371 % (pythonw, target)) 

372 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("", EWS)), 

373 "", 0, winreg.REG_SZ, '"%s" "%%1"' % spyder) 

374 winreg.SetValueEx(winreg.CreateKey(root, KEY_C2 % ("NoCon", EWS)), 

375 "", 0, winreg.REG_SZ, '"%s" "%%1"' % spyder) 

376 

377 # Drop support 

378 handler = "{60254CA5-953B-11CF-8C96-00AA00B8708C}" 

379 for ftype in ("", "NoCon", "Compiled"): 

380 winreg.SetValueEx(winreg.CreateKey(root, KEY_DROP1 % ftype), 

381 "", 0, winreg.REG_SZ, handler) 

382 

383 # Icons 

384 dlls = osp.join(target, 'DLLs') 

385 winreg.SetValueEx(winreg.CreateKey(root, KEY_I % ""), 

386 "", 0, winreg.REG_SZ, r'%s\py.ico' % dlls) 

387 winreg.SetValueEx(winreg.CreateKey(root, KEY_I % "NoCon"), 

388 "", 0, winreg.REG_SZ, r'%s\py.ico' % dlls) 

389 winreg.SetValueEx(winreg.CreateKey(root, KEY_I % "Compiled"), 

390 "", 0, winreg.REG_SZ, r'%s\pyc.ico' % dlls) 

391 

392 # Descriptions 

393 winreg.SetValueEx(winreg.CreateKey(root, KEY_D % ""), 

394 "", 0, winreg.REG_SZ, "Python File") 

395 winreg.SetValueEx(winreg.CreateKey(root, KEY_D % "NoCon"), 

396 "", 0, winreg.REG_SZ, "Python File (no console)") 

397 winreg.SetValueEx(winreg.CreateKey(root, KEY_D % "Compiled"), 

398 "", 0, winreg.REG_SZ, "Compiled Python File") 

399 

400 # PythonCore entries 

401 ''' 

402 short_version = utils.get_python_infos(target)[0] 

403 long_version = utils.get_python_long_version(target) 

404 key_core = (KEY_S1 % short_version) + r'\\%s' 

405 winreg.SetValueEx(winreg.CreateKey(root, key_core % 'InstallPath'), 

406 "", 0, winreg.REG_SZ, target) 

407 winreg.SetValueEx(winreg.CreateKey(root, 

408 key_core % r'InstallPath\\InstallGroup'), 

409 "", 0, winreg.REG_SZ, "Python %s" % short_version) 

410 winreg.SetValueEx(winreg.CreateKey(root, key_core % 'Modules'), 

411 "", 0, winreg.REG_SZ, "") 

412 winreg.SetValueEx(winreg.CreateKey(root, key_core % 'PythonPath'), 

413 "", 0, winreg.REG_SZ, 

414 r"%s\\Lib;%s\\DLLs" % (target, target)) 

415 winreg.SetValueEx(winreg.CreateKey(root, 

416 key_core % r'Help\\Main Python Documentation'), 

417 "", 0, winreg.REG_SZ, 

418 r"%s\\Doc\\python%s.chm" % (target, long_version)) 

419 ''' 

420 

421 # Create start menu entries for all WinPython launchers 

422 ''' 

423 for path, desc, fname in _get_shortcut_data(target, current=current): 

424 utils.create_shortcut(path, desc, fname) 

425 ''' 

426 

427 # Register the Python ActiveX Scripting client (requires pywin32) 

428 axscript = osp.join(target, 'Lib', 'site-packages', 'win32comext', 

429 'axscript', 'client', 'pyscript.py') 

430 if osp.isfile(axscript): 

431 subprocess.call('"%s" "%s"' % (python, axscript), cwd=target) 

432 else: 

433 print('Unable to register ActiveX: please install pywin32', 

434 file=sys.stderr) 

435 

436 

437''' 

438def unregister(target, current=True): 

439 """Unregister a Python distribution in Windows registry""" 

440 # Registry entries 

441 root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE 

442 short_version = utils.get_python_infos(target)[0] 

443 key_core = (KEY_S1 % short_version) + r'\\%s' 

444 for key in ( 

445 # Drop support 

446 KEY_DROP1 % "", KEY_DROP1 % "NoCon", KEY_DROP1 % "Compiled", 

447 KEY_DROP0 % "", KEY_DROP0 % "NoCon", KEY_DROP0 % "Compiled", 

448 # Icons 

449 KEY_I % "NoCon", KEY_I % "Compiled", KEY_I % "", 

450 # Edit with IDLE 

451 KEY_C2 % ("", EWI), KEY_C2 % ("NoCon", EWI), 

452 KEY_C1 % ("", EWI), KEY_C1 % ("NoCon", EWI), 

453 # Edit with Spyder 

454 KEY_C2 % ("", EWS), KEY_C2 % ("NoCon", EWS), 

455 KEY_C1 % ("", EWS), KEY_C1 % ("NoCon", EWS), 

456 # Verbs 

457 KEY_C2 % ("", "open"), 

458 KEY_C2 % ("NoCon", "open"), 

459 KEY_C2 % ("Compiled", "open"), 

460 KEY_C1 % ("", "open"), 

461 KEY_C1 % ("NoCon", "open"), 

462 KEY_C1 % ("Compiled", "open"), 

463 KEY_C0 % "", KEY_C0 % "NoCon", KEY_C0 % "Compiled", 

464 # Descriptions 

465 KEY_D % "NoCon", KEY_D % "Compiled", KEY_D % "", 

466 # PythonCore 

467 key_core % r'InstallPath\\InstallGroup', 

468 key_core % 'InstallPath', 

469 key_core % 'Modules', 

470 key_core % 'PythonPath', 

471 key_core % r'Help\\Main Python Documentation', 

472 key_core % 'Help', 

473 KEY_S1 % short_version, KEY_S0, KEY_S, 

474 ): 

475 try: 

476 print(key) 

477 winreg.DeleteKey(root, key) 

478 except WindowsError: 

479 rootkey = 'HKEY_CURRENT_USER' if current else 'HKEY_LOCAL_MACHINE' 

480 print(r'Unable to remove %s\\%s' % (rootkey, key), file=sys.stderr) 

481 

482 # Start menu shortcuts 

483 for path, desc, fname in _get_shortcut_data(target, current=current): 

484 if osp.exists(fname): 

485 os.remove(fname) 

486'''