# -*- coding: utf-8 -*-
#
# Copyright © 2012 Pierre Raybaut
# Licensed under the terms of the MIT License
# (see winpython/__init__.py for details)
"""
Register a Python distribution
Created on Tue Aug 21 21:46:30 2012
"""
import sys
import os
from pathlib import Path
import platform
import importlib
# import subprocess
# Local imports
import winreg
from winpython import utils
KEY_C = r"Software\Classes\%s"
KEY_C0 = KEY_C % r"Python.%sFile\shell"
KEY_C1 = KEY_C % r"Python.%sFile\shell\%s"
KEY_C2 = KEY_C1 + r"\command"
KEY_DROP0 = KEY_C % r"Python.%sFile\shellex"
KEY_DROP1 = KEY_C % r"Python.%sFile\shellex\DropHandler"
KEY_I = KEY_C % r"Python.%sFile\DefaultIcon"
KEY_D = KEY_C % r"Python.%sFile"
EWI = "Edit with IDLE"
EWS = "Edit with Spyder"
KEY_S = r"Software\Python"
KEY_S0 = KEY_S + r"\WinPython" # was PythonCore before PEP-0514
KEY_S1 = KEY_S0 + r"\%s"
def _remove_start_menu_folder(target, current=True):
"remove menu Folder for target WinPython"
import importlib.util
win32com_exists = importlib.util.find_spec('win32com') is not None
# we return nothing if no win32com package
if win32com_exists:
utils.remove_winpython_start_menu_folder(current=current)
def _get_shortcut_data(target, current=True):
"get windows menu access, if win32com exists otherwise nothing"
import importlib.util
win32com_exists = importlib.util.find_spec('win32com') is not None
# we return nothing if no win32com package
if not win32com_exists:
return []
wpgroup = utils.create_winpython_start_menu_folder(current=current)
wpdir = str(Path(target).parent)
data = []
for name in os.listdir(wpdir):
bname, ext = Path(name).stem, Path(name).suffix
if ext == ".exe":
data.append(
(
str(Path(wpdir) / name),
bname,
str(Path(wpgroup) / bname),
)
)
return data
def register(target, current=True, verbose=True):
"""Register a Python distribution in Windows registry"""
root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE
# Creating Registry entries
if verbose:
print(f'Creating WinPython registry entries for {target}')
# Extensions
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".py"),
"",
0,
winreg.REG_SZ,
"Python.File",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".pyw"),
"",
0,
winreg.REG_SZ,
"Python.NoConFile",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".pyc"),
"",
0,
winreg.REG_SZ,
"Python.CompiledFile",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".pyo"),
"",
0,
winreg.REG_SZ,
"Python.CompiledFile",
)
# MIME types
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".py"),
"Content Type",
0,
winreg.REG_SZ,
"text/plain",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C % ".pyw"),
"Content Type",
0,
winreg.REG_SZ,
"text/plain",
)
# Verbs
python = str((Path(target) / "python.exe").resolve())
pythonw = str((Path(target) / "pythonw.exe").resolve())
spyder = str((Path(target).parent / "Spyder.exe").resolve())
if not Path(spyder).is_file():
spyder = f'{pythonw}" "{target}\Scripts\spyder'
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("", "open")),
"",
0,
winreg.REG_SZ,
'"%s" "%%1" %%*' % python,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("NoCon", "open")),
"",
0,
winreg.REG_SZ,
'"%s" "%%1" %%*' % pythonw,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("Compiled", "open")),
"",
0,
winreg.REG_SZ,
'"%s" "%%1" %%*' % python,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("", EWI)),
"",
0,
winreg.REG_SZ,
'"%s" "%s\Lib\idlelib\idle.pyw" -n -e "%%1"' % (pythonw, target),
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("NoCon", EWI)),
"",
0,
winreg.REG_SZ,
'"%s" "%s\Lib\idlelib\idle.pyw" -n -e "%%1"' % (pythonw, target),
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("", EWS)),
"",
0,
winreg.REG_SZ,
'"%s" "%%1"' % spyder,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_C2 % ("NoCon", EWS)),
"",
0,
winreg.REG_SZ,
'"%s" "%%1"' % spyder,
)
# Drop support
handler = "{60254CA5-953B-11CF-8C96-00AA00B8708C}"
for ftype in ("", "NoCon", "Compiled"):
winreg.SetValueEx(
winreg.CreateKey(root, KEY_DROP1 % ftype),
"",
0,
winreg.REG_SZ,
handler,
)
# Icons
dlls = str(Path(target) / "DLLs")
winreg.SetValueEx(
winreg.CreateKey(root, KEY_I % ""),
"",
0,
winreg.REG_SZ,
r"%s\py.ico" % dlls,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_I % "NoCon"),
"",
0,
winreg.REG_SZ,
r"%s\py.ico" % dlls,
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_I % "Compiled"),
"",
0,
winreg.REG_SZ,
r"%s\pyc.ico" % dlls,
)
# Descriptions
winreg.SetValueEx(
winreg.CreateKey(root, KEY_D % ""),
"",
0,
winreg.REG_SZ,
"Python File",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_D % "NoCon"),
"",
0,
winreg.REG_SZ,
"Python File (no console)",
)
winreg.SetValueEx(
winreg.CreateKey(root, KEY_D % "Compiled"),
"",
0,
winreg.REG_SZ,
"Compiled Python File",
)
# PythonCore entries
python_infos = utils.get_python_infos(target) # ('3.11', 64)
short_version = python_infos[0] # 3.11 from ('3.11', 64)
long_version = utils.get_python_long_version(target) # 3.11.5
key_core = (KEY_S1 % short_version) + r"\%s" # Winpython\3.11
# PEP-0514 additions, with standard Python practice
SupportUrl="https://winpython.github.io"
SysArchitecture = platform.architecture()[0] # '64bit'
SysVersion = '.'.join(platform.python_version_tuple()[:2]) # '3.11'
Version = platform.python_version() # '3.11.5'
# But keep consistent with past possibilities until more alignement
SysArchitecture = f'{python_infos[1]}bit' # '64bit'
SysVersion = short_version
Version = long_version
DisplayName = f'Python {Version} ({SysArchitecture})'
key_short = (KEY_S1 % short_version) # WinPython\3.11
key_keys={'DisplayName':DisplayName,
'SupportUrl':SupportUrl,
'SysVersion':SysVersion,
'SysArchitecture':SysArchitecture,
'Version':Version}
regkey = winreg.CreateKey(root, key_short)
# see https://www.programcreek.com/python/example/106949/winreg.CreateKey
# winreg.SetValueEx(key, '', reg.REG_SZ, '')
for k, v in key_keys.items():
winreg.SetValueEx(
regkey,
k,
0,
winreg.REG_SZ,
v,
)
winreg.CloseKey(regkey)
# pep-0514 additions at InstallPathLevel
ExecutablePath = python
WindowedExecutablePath = pythonw
key_short = key_core % "InstallPath" # WinPython\3.11\InstallPath
key_keys={'ExecutablePath':ExecutablePath,
'WindowedExecutablePath':WindowedExecutablePath}
regkey = winreg.CreateKey(root, key_core % "InstallPath")
winreg.SetValueEx(
regkey,
"",
0,
winreg.REG_SZ,
target + '\\',
)
for k, v in key_keys.items():
winreg.SetValueEx(
regkey,
k,
0,
winreg.REG_SZ,
v,
)
winreg.CloseKey(regkey)
winreg.SetValueEx(
winreg.CreateKey(root, key_core % r"InstallPath\InstallGroup"),
"",
0,
winreg.REG_SZ,
"Python %s" % short_version,
)
winreg.SetValueEx(
winreg.CreateKey(root, key_core % "Modules"),
"",
0,
winreg.REG_SZ,
"",
)
winreg.SetValueEx(
winreg.CreateKey(root, key_core % "PythonPath"),
"",
0,
winreg.REG_SZ,
r"%s\Lib;%s\DLLs" % (target, target),
)
winreg.SetValueEx(
winreg.CreateKey(
root,
key_core % r"Help\Main Python Documentation",
),
"",
0,
winreg.REG_SZ,
r"%s\Doc\python%s.chm" % (target, long_version),
)
# Create start menu entries for all WinPython launchers
spec = importlib.util.find_spec('pythoncom')
if verbose and spec is None:
print(f"Can't create WinPython menu as pywin32 package is not installed")
if verbose and spec is not None:
print(f'Creating WinPython menu for all icons in {target}')
for path, desc, fname in _get_shortcut_data(target, current=current):
utils.create_shortcut(path, desc, fname, verbose=verbose)
def unregister(target, current=True, verbose=True):
"""Unregister a Python distribution in Windows registry"""
# Removing Registry entries
if verbose:
print(f'Removing WinPython registry entries for {target}')
root = winreg.HKEY_CURRENT_USER if current else winreg.HKEY_LOCAL_MACHINE
short_version = utils.get_python_infos(target)[0]
key_core = (KEY_S1 % short_version) + r"\%s"
for key in (
# Drop support
KEY_DROP1 % "",
KEY_DROP1 % "NoCon",
KEY_DROP1 % "Compiled",
KEY_DROP0 % "",
KEY_DROP0 % "NoCon",
KEY_DROP0 % "Compiled",
# Icons
KEY_I % "NoCon",
KEY_I % "Compiled",
KEY_I % "",
# Edit with IDLE
KEY_C2 % ("", EWI),
KEY_C2 % ("NoCon", EWI),
KEY_C1 % ("", EWI),
KEY_C1 % ("NoCon", EWI),
# Edit with Spyder
KEY_C2 % ("", EWS),
KEY_C2 % ("NoCon", EWS),
KEY_C1 % ("", EWS),
KEY_C1 % ("NoCon", EWS),
# Verbs
KEY_C2 % ("", "open"),
KEY_C2 % ("NoCon", "open"),
KEY_C2 % ("Compiled", "open"),
KEY_C1 % ("", "open"),
KEY_C1 % ("NoCon", "open"),
KEY_C1 % ("Compiled", "open"),
KEY_C0 % "",
KEY_C0 % "NoCon",
KEY_C0 % "Compiled",
# Descriptions
KEY_D % "NoCon",
KEY_D % "Compiled",
KEY_D % "",
# PythonCore
key_core % r"InstallPath\InstallGroup",
key_core % "InstallPath",
key_core % "Modules",
key_core % "PythonPath",
key_core % r"Help\Main Python Documentation",
key_core % "Help",
KEY_S1 % short_version,
KEY_S0,
KEY_S,
):
try:
if verbose:
print(key)
winreg.DeleteKey(root, key)
except WindowsError:
rootkey = "HKEY_CURRENT_USER" if current else "HKEY_LOCAL_MACHINE"
if verbose:
print(
r"Unable to remove %s\%s" % (rootkey, key),
file=sys.stderr,
)
# remove menu shortcuts
spec = importlib.util.find_spec('pythoncom')
if verbose and spec is None:
print(f"Can't remove WinPython menu as pywin32 package is not installed")
if verbose and spec is not None:
print(f'Removing WinPython menu for all icons in {target}')
_remove_start_menu_folder(target, current=current)
#for path, desc, fname in _get_shortcut_data(target, current=current):
# if Path(fname).exists():
# os.remove(fname)
if __name__ == "__main__":
register(sys.prefix)
unregister(sys.prefix)