784 lines
27 KiB
Python
784 lines
27 KiB
Python
|
# postinstall script for pywin32
|
||
|
#
|
||
|
# copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory,
|
||
|
# and creates a pth file
|
||
|
import glob
|
||
|
import os
|
||
|
import shutil
|
||
|
import sys
|
||
|
import sysconfig
|
||
|
|
||
|
try:
|
||
|
import winreg as winreg
|
||
|
except:
|
||
|
import winreg
|
||
|
|
||
|
# Send output somewhere so it can be found if necessary...
|
||
|
import tempfile
|
||
|
|
||
|
tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w")
|
||
|
|
||
|
|
||
|
class Tee:
|
||
|
def __init__(self, file):
|
||
|
self.f = file
|
||
|
|
||
|
def write(self, what):
|
||
|
if self.f is not None:
|
||
|
try:
|
||
|
self.f.write(what.replace("\n", "\r\n"))
|
||
|
except IOError:
|
||
|
pass
|
||
|
tee_f.write(what)
|
||
|
|
||
|
def flush(self):
|
||
|
if self.f is not None:
|
||
|
try:
|
||
|
self.f.flush()
|
||
|
except IOError:
|
||
|
pass
|
||
|
tee_f.flush()
|
||
|
|
||
|
|
||
|
# For some unknown reason, when running under bdist_wininst we will start up
|
||
|
# with sys.stdout as None but stderr is hooked up. This work-around allows
|
||
|
# bdist_wininst to see the output we write and display it at the end of
|
||
|
# the install.
|
||
|
if sys.stdout is None:
|
||
|
sys.stdout = sys.stderr
|
||
|
|
||
|
sys.stderr = Tee(sys.stderr)
|
||
|
sys.stdout = Tee(sys.stdout)
|
||
|
|
||
|
com_modules = [
|
||
|
# module_name, class_names
|
||
|
("win32com.servers.interp", "Interpreter"),
|
||
|
("win32com.servers.dictionary", "DictionaryPolicy"),
|
||
|
("win32com.axscript.client.pyscript", "PyScript"),
|
||
|
]
|
||
|
|
||
|
# Is this a 'silent' install - ie, avoid all dialogs.
|
||
|
# Different than 'verbose'
|
||
|
silent = 0
|
||
|
|
||
|
# Verbosity of output messages.
|
||
|
verbose = 1
|
||
|
|
||
|
root_key_name = "Software\\Python\\PythonCore\\" + sys.winver
|
||
|
|
||
|
try:
|
||
|
# When this script is run from inside the bdist_wininst installer,
|
||
|
# file_created() and directory_created() are additional builtin
|
||
|
# functions which write lines to Python23\pywin32-install.log. This is
|
||
|
# a list of actions for the uninstaller, the format is inspired by what
|
||
|
# the Wise installer also creates.
|
||
|
file_created
|
||
|
is_bdist_wininst = True
|
||
|
except NameError:
|
||
|
is_bdist_wininst = False # we know what it is not - but not what it is :)
|
||
|
|
||
|
def file_created(file):
|
||
|
pass
|
||
|
|
||
|
def directory_created(directory):
|
||
|
pass
|
||
|
|
||
|
def get_root_hkey():
|
||
|
try:
|
||
|
winreg.OpenKey(
|
||
|
winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY
|
||
|
)
|
||
|
return winreg.HKEY_LOCAL_MACHINE
|
||
|
except OSError:
|
||
|
# Either not exist, or no permissions to create subkey means
|
||
|
# must be HKCU
|
||
|
return winreg.HKEY_CURRENT_USER
|
||
|
|
||
|
|
||
|
try:
|
||
|
create_shortcut
|
||
|
except NameError:
|
||
|
# Create a function with the same signature as create_shortcut provided
|
||
|
# by bdist_wininst
|
||
|
def create_shortcut(
|
||
|
path, description, filename, arguments="", workdir="", iconpath="", iconindex=0
|
||
|
):
|
||
|
import pythoncom
|
||
|
from win32com.shell import shell
|
||
|
|
||
|
ilink = pythoncom.CoCreateInstance(
|
||
|
shell.CLSID_ShellLink,
|
||
|
None,
|
||
|
pythoncom.CLSCTX_INPROC_SERVER,
|
||
|
shell.IID_IShellLink,
|
||
|
)
|
||
|
ilink.SetPath(path)
|
||
|
ilink.SetDescription(description)
|
||
|
if arguments:
|
||
|
ilink.SetArguments(arguments)
|
||
|
if workdir:
|
||
|
ilink.SetWorkingDirectory(workdir)
|
||
|
if iconpath or iconindex:
|
||
|
ilink.SetIconLocation(iconpath, iconindex)
|
||
|
# now save it.
|
||
|
ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile)
|
||
|
ipf.Save(filename, 0)
|
||
|
|
||
|
# Support the same list of "path names" as bdist_wininst.
|
||
|
def get_special_folder_path(path_name):
|
||
|
from win32com.shell import shell, shellcon
|
||
|
|
||
|
for maybe in """
|
||
|
CSIDL_COMMON_STARTMENU CSIDL_STARTMENU CSIDL_COMMON_APPDATA
|
||
|
CSIDL_LOCAL_APPDATA CSIDL_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY
|
||
|
CSIDL_DESKTOPDIRECTORY CSIDL_COMMON_STARTUP CSIDL_STARTUP
|
||
|
CSIDL_COMMON_PROGRAMS CSIDL_PROGRAMS CSIDL_PROGRAM_FILES_COMMON
|
||
|
CSIDL_PROGRAM_FILES CSIDL_FONTS""".split():
|
||
|
if maybe == path_name:
|
||
|
csidl = getattr(shellcon, maybe)
|
||
|
return shell.SHGetSpecialFolderPath(0, csidl, False)
|
||
|
raise ValueError("%s is an unknown path ID" % (path_name,))
|
||
|
|
||
|
|
||
|
def CopyTo(desc, src, dest):
|
||
|
import win32api
|
||
|
import win32con
|
||
|
|
||
|
while 1:
|
||
|
try:
|
||
|
win32api.CopyFile(src, dest, 0)
|
||
|
return
|
||
|
except win32api.error as details:
|
||
|
if details.winerror == 5: # access denied - user not admin.
|
||
|
raise
|
||
|
if silent:
|
||
|
# Running silent mode - just re-raise the error.
|
||
|
raise
|
||
|
full_desc = (
|
||
|
"Error %s\n\n"
|
||
|
"If you have any Python applications running, "
|
||
|
"please close them now\nand select 'Retry'\n\n%s"
|
||
|
% (desc, details.strerror)
|
||
|
)
|
||
|
rc = win32api.MessageBox(
|
||
|
0, full_desc, "Installation Error", win32con.MB_ABORTRETRYIGNORE
|
||
|
)
|
||
|
if rc == win32con.IDABORT:
|
||
|
raise
|
||
|
elif rc == win32con.IDIGNORE:
|
||
|
return
|
||
|
# else retry - around we go again.
|
||
|
|
||
|
|
||
|
# We need to import win32api to determine the Windows system directory,
|
||
|
# so we can copy our system files there - but importing win32api will
|
||
|
# load the pywintypes.dll already in the system directory preventing us
|
||
|
# from updating them!
|
||
|
# So, we pull the same trick pywintypes.py does, but it loads from
|
||
|
# our pywintypes_system32 directory.
|
||
|
def LoadSystemModule(lib_dir, modname):
|
||
|
# See if this is a debug build.
|
||
|
import importlib.machinery
|
||
|
import importlib.util
|
||
|
|
||
|
suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else ""
|
||
|
filename = "%s%d%d%s.dll" % (
|
||
|
modname,
|
||
|
sys.version_info[0],
|
||
|
sys.version_info[1],
|
||
|
suffix,
|
||
|
)
|
||
|
filename = os.path.join(lib_dir, "pywin32_system32", filename)
|
||
|
loader = importlib.machinery.ExtensionFileLoader(modname, filename)
|
||
|
spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename)
|
||
|
mod = importlib.util.module_from_spec(spec)
|
||
|
spec.loader.exec_module(mod)
|
||
|
|
||
|
|
||
|
def SetPyKeyVal(key_name, value_name, value):
|
||
|
root_hkey = get_root_hkey()
|
||
|
root_key = winreg.OpenKey(root_hkey, root_key_name)
|
||
|
try:
|
||
|
my_key = winreg.CreateKey(root_key, key_name)
|
||
|
try:
|
||
|
winreg.SetValueEx(my_key, value_name, 0, winreg.REG_SZ, value)
|
||
|
if verbose:
|
||
|
print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value))
|
||
|
finally:
|
||
|
my_key.Close()
|
||
|
finally:
|
||
|
root_key.Close()
|
||
|
|
||
|
|
||
|
def UnsetPyKeyVal(key_name, value_name, delete_key=False):
|
||
|
root_hkey = get_root_hkey()
|
||
|
root_key = winreg.OpenKey(root_hkey, root_key_name)
|
||
|
try:
|
||
|
my_key = winreg.OpenKey(root_key, key_name, 0, winreg.KEY_SET_VALUE)
|
||
|
try:
|
||
|
winreg.DeleteValue(my_key, value_name)
|
||
|
if verbose:
|
||
|
print("-> DELETE %s\\%s[%s]" % (root_key_name, key_name, value_name))
|
||
|
finally:
|
||
|
my_key.Close()
|
||
|
if delete_key:
|
||
|
winreg.DeleteKey(root_key, key_name)
|
||
|
if verbose:
|
||
|
print("-> DELETE %s\\%s" % (root_key_name, key_name))
|
||
|
except OSError as why:
|
||
|
winerror = getattr(why, "winerror", why.errno)
|
||
|
if winerror != 2: # file not found
|
||
|
raise
|
||
|
finally:
|
||
|
root_key.Close()
|
||
|
|
||
|
|
||
|
def RegisterCOMObjects(register=True):
|
||
|
import win32com.server.register
|
||
|
|
||
|
if register:
|
||
|
func = win32com.server.register.RegisterClasses
|
||
|
else:
|
||
|
func = win32com.server.register.UnregisterClasses
|
||
|
flags = {}
|
||
|
if not verbose:
|
||
|
flags["quiet"] = 1
|
||
|
for module, klass_name in com_modules:
|
||
|
__import__(module)
|
||
|
mod = sys.modules[module]
|
||
|
flags["finalize_register"] = getattr(mod, "DllRegisterServer", None)
|
||
|
flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None)
|
||
|
klass = getattr(mod, klass_name)
|
||
|
func(klass, **flags)
|
||
|
|
||
|
|
||
|
def RegisterHelpFile(register=True, lib_dir=None):
|
||
|
if lib_dir is None:
|
||
|
lib_dir = sysconfig.get_paths()["platlib"]
|
||
|
if register:
|
||
|
# Register the .chm help file.
|
||
|
chm_file = os.path.join(lib_dir, "PyWin32.chm")
|
||
|
if os.path.isfile(chm_file):
|
||
|
# This isn't recursive, so if 'Help' doesn't exist, we croak
|
||
|
SetPyKeyVal("Help", None, None)
|
||
|
SetPyKeyVal("Help\\Pythonwin Reference", None, chm_file)
|
||
|
return chm_file
|
||
|
else:
|
||
|
print("NOTE: PyWin32.chm can not be located, so has not " "been registered")
|
||
|
else:
|
||
|
UnsetPyKeyVal("Help\\Pythonwin Reference", None, delete_key=True)
|
||
|
return None
|
||
|
|
||
|
|
||
|
def RegisterPythonwin(register=True, lib_dir=None):
|
||
|
"""Add (or remove) Pythonwin to context menu for python scripts.
|
||
|
??? Should probably also add Edit command for pys files also.
|
||
|
Also need to remove these keys on uninstall, but there's no function
|
||
|
like file_created to add registry entries to uninstall log ???
|
||
|
"""
|
||
|
import os
|
||
|
|
||
|
if lib_dir is None:
|
||
|
lib_dir = sysconfig.get_paths()["platlib"]
|
||
|
classes_root = get_root_hkey()
|
||
|
## Installer executable doesn't seem to pass anything to postinstall script indicating if it's a debug build,
|
||
|
pythonwin_exe = os.path.join(lib_dir, "Pythonwin", "Pythonwin.exe")
|
||
|
pythonwin_edit_command = pythonwin_exe + ' -edit "%1"'
|
||
|
|
||
|
keys_vals = [
|
||
|
(
|
||
|
"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Pythonwin.exe",
|
||
|
"",
|
||
|
pythonwin_exe,
|
||
|
),
|
||
|
(
|
||
|
"Software\\Classes\\Python.File\\shell\\Edit with Pythonwin",
|
||
|
"command",
|
||
|
pythonwin_edit_command,
|
||
|
),
|
||
|
(
|
||
|
"Software\\Classes\\Python.NoConFile\\shell\\Edit with Pythonwin",
|
||
|
"command",
|
||
|
pythonwin_edit_command,
|
||
|
),
|
||
|
]
|
||
|
|
||
|
try:
|
||
|
if register:
|
||
|
for key, sub_key, val in keys_vals:
|
||
|
## Since winreg only uses the character Api functions, this can fail if Python
|
||
|
## is installed to a path containing non-ascii characters
|
||
|
hkey = winreg.CreateKey(classes_root, key)
|
||
|
if sub_key:
|
||
|
hkey = winreg.CreateKey(hkey, sub_key)
|
||
|
winreg.SetValueEx(hkey, None, 0, winreg.REG_SZ, val)
|
||
|
hkey.Close()
|
||
|
else:
|
||
|
for key, sub_key, val in keys_vals:
|
||
|
try:
|
||
|
if sub_key:
|
||
|
hkey = winreg.OpenKey(classes_root, key)
|
||
|
winreg.DeleteKey(hkey, sub_key)
|
||
|
hkey.Close()
|
||
|
winreg.DeleteKey(classes_root, key)
|
||
|
except OSError as why:
|
||
|
winerror = getattr(why, "winerror", why.errno)
|
||
|
if winerror != 2: # file not found
|
||
|
raise
|
||
|
finally:
|
||
|
# tell windows about the change
|
||
|
from win32com.shell import shell, shellcon
|
||
|
|
||
|
shell.SHChangeNotify(
|
||
|
shellcon.SHCNE_ASSOCCHANGED, shellcon.SHCNF_IDLIST, None, None
|
||
|
)
|
||
|
|
||
|
|
||
|
def get_shortcuts_folder():
|
||
|
if get_root_hkey() == winreg.HKEY_LOCAL_MACHINE:
|
||
|
try:
|
||
|
fldr = get_special_folder_path("CSIDL_COMMON_PROGRAMS")
|
||
|
except OSError:
|
||
|
# No CSIDL_COMMON_PROGRAMS on this platform
|
||
|
fldr = get_special_folder_path("CSIDL_PROGRAMS")
|
||
|
else:
|
||
|
# non-admin install - always goes in this user's start menu.
|
||
|
fldr = get_special_folder_path("CSIDL_PROGRAMS")
|
||
|
|
||
|
try:
|
||
|
install_group = winreg.QueryValue(
|
||
|
get_root_hkey(), root_key_name + "\\InstallPath\\InstallGroup"
|
||
|
)
|
||
|
except OSError:
|
||
|
vi = sys.version_info
|
||
|
install_group = "Python %d.%d" % (vi[0], vi[1])
|
||
|
return os.path.join(fldr, install_group)
|
||
|
|
||
|
|
||
|
# Get the system directory, which may be the Wow64 directory if we are a 32bit
|
||
|
# python on a 64bit OS.
|
||
|
def get_system_dir():
|
||
|
import win32api # we assume this exists.
|
||
|
|
||
|
try:
|
||
|
import pythoncom
|
||
|
import win32process
|
||
|
from win32com.shell import shell, shellcon
|
||
|
|
||
|
try:
|
||
|
if win32process.IsWow64Process():
|
||
|
return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEMX86)
|
||
|
return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEM)
|
||
|
except (pythoncom.com_error, win32process.error):
|
||
|
return win32api.GetSystemDirectory()
|
||
|
except ImportError:
|
||
|
return win32api.GetSystemDirectory()
|
||
|
|
||
|
|
||
|
def fixup_dbi():
|
||
|
# We used to have a dbi.pyd with our .pyd files, but now have a .py file.
|
||
|
# If the user didn't uninstall, they will find the .pyd which will cause
|
||
|
# problems - so handle that.
|
||
|
import win32api
|
||
|
import win32con
|
||
|
|
||
|
pyd_name = os.path.join(os.path.dirname(win32api.__file__), "dbi.pyd")
|
||
|
pyd_d_name = os.path.join(os.path.dirname(win32api.__file__), "dbi_d.pyd")
|
||
|
py_name = os.path.join(os.path.dirname(win32con.__file__), "dbi.py")
|
||
|
for this_pyd in (pyd_name, pyd_d_name):
|
||
|
this_dest = this_pyd + ".old"
|
||
|
if os.path.isfile(this_pyd) and os.path.isfile(py_name):
|
||
|
try:
|
||
|
if os.path.isfile(this_dest):
|
||
|
print(
|
||
|
"Old dbi '%s' already exists - deleting '%s'"
|
||
|
% (this_dest, this_pyd)
|
||
|
)
|
||
|
os.remove(this_pyd)
|
||
|
else:
|
||
|
os.rename(this_pyd, this_dest)
|
||
|
print("renamed '%s'->'%s.old'" % (this_pyd, this_pyd))
|
||
|
file_created(this_pyd + ".old")
|
||
|
except os.error as exc:
|
||
|
print("FAILED to rename '%s': %s" % (this_pyd, exc))
|
||
|
|
||
|
|
||
|
def install(lib_dir):
|
||
|
import traceback
|
||
|
|
||
|
# The .pth file is now installed as a regular file.
|
||
|
# Create the .pth file in the site-packages dir, and use only relative paths
|
||
|
# We used to write a .pth directly to sys.prefix - clobber it.
|
||
|
if os.path.isfile(os.path.join(sys.prefix, "pywin32.pth")):
|
||
|
os.unlink(os.path.join(sys.prefix, "pywin32.pth"))
|
||
|
# The .pth may be new and therefore not loaded in this session.
|
||
|
# Setup the paths just in case.
|
||
|
for name in "win32 win32\\lib Pythonwin".split():
|
||
|
sys.path.append(os.path.join(lib_dir, name))
|
||
|
# It is possible people with old versions installed with still have
|
||
|
# pywintypes and pythoncom registered. We no longer need this, and stale
|
||
|
# entries hurt us.
|
||
|
for name in "pythoncom pywintypes".split():
|
||
|
keyname = "Software\\Python\\PythonCore\\" + sys.winver + "\\Modules\\" + name
|
||
|
for root in winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER:
|
||
|
try:
|
||
|
winreg.DeleteKey(root, keyname + "\\Debug")
|
||
|
except WindowsError:
|
||
|
pass
|
||
|
try:
|
||
|
winreg.DeleteKey(root, keyname)
|
||
|
except WindowsError:
|
||
|
pass
|
||
|
LoadSystemModule(lib_dir, "pywintypes")
|
||
|
LoadSystemModule(lib_dir, "pythoncom")
|
||
|
import win32api
|
||
|
|
||
|
# and now we can get the system directory:
|
||
|
files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*"))
|
||
|
if not files:
|
||
|
raise RuntimeError("No system files to copy!!")
|
||
|
# Try the system32 directory first - if that fails due to "access denied",
|
||
|
# it implies a non-admin user, and we use sys.prefix
|
||
|
for dest_dir in [get_system_dir(), sys.prefix]:
|
||
|
# and copy some files over there
|
||
|
worked = 0
|
||
|
try:
|
||
|
for fname in files:
|
||
|
base = os.path.basename(fname)
|
||
|
dst = os.path.join(dest_dir, base)
|
||
|
CopyTo("installing %s" % base, fname, dst)
|
||
|
if verbose:
|
||
|
print("Copied %s to %s" % (base, dst))
|
||
|
# Register the files with the uninstaller
|
||
|
file_created(dst)
|
||
|
worked = 1
|
||
|
# Nuke any other versions that may exist - having
|
||
|
# duplicates causes major headaches.
|
||
|
bad_dest_dirs = [
|
||
|
os.path.join(sys.prefix, "Library\\bin"),
|
||
|
os.path.join(sys.prefix, "Lib\\site-packages\\win32"),
|
||
|
]
|
||
|
if dest_dir != sys.prefix:
|
||
|
bad_dest_dirs.append(sys.prefix)
|
||
|
for bad_dest_dir in bad_dest_dirs:
|
||
|
bad_fname = os.path.join(bad_dest_dir, base)
|
||
|
if os.path.exists(bad_fname):
|
||
|
# let exceptions go here - delete must succeed
|
||
|
os.unlink(bad_fname)
|
||
|
if worked:
|
||
|
break
|
||
|
except win32api.error as details:
|
||
|
if details.winerror == 5:
|
||
|
# access denied - user not admin - try sys.prefix dir,
|
||
|
# but first check that a version doesn't already exist
|
||
|
# in that place - otherwise that one will still get used!
|
||
|
if os.path.exists(dst):
|
||
|
msg = (
|
||
|
"The file '%s' exists, but can not be replaced "
|
||
|
"due to insufficient permissions. You must "
|
||
|
"reinstall this software as an Administrator" % dst
|
||
|
)
|
||
|
print(msg)
|
||
|
raise RuntimeError(msg)
|
||
|
continue
|
||
|
raise
|
||
|
else:
|
||
|
raise RuntimeError(
|
||
|
"You don't have enough permissions to install the system files"
|
||
|
)
|
||
|
|
||
|
# Pythonwin 'compiles' config files - record them for uninstall.
|
||
|
pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin")
|
||
|
for fname in glob.glob(os.path.join(pywin_dir, "*.cfg")):
|
||
|
file_created(fname[:-1] + "c") # .cfg->.cfc
|
||
|
|
||
|
# Register our demo COM objects.
|
||
|
try:
|
||
|
try:
|
||
|
RegisterCOMObjects()
|
||
|
except win32api.error as details:
|
||
|
if details.winerror != 5: # ERROR_ACCESS_DENIED
|
||
|
raise
|
||
|
print("You do not have the permissions to install COM objects.")
|
||
|
print("The sample COM objects were not registered.")
|
||
|
except Exception:
|
||
|
print("FAILED to register the Python COM objects")
|
||
|
traceback.print_exc()
|
||
|
|
||
|
# There may be no main Python key in HKCU if, eg, an admin installed
|
||
|
# python itself.
|
||
|
winreg.CreateKey(get_root_hkey(), root_key_name)
|
||
|
|
||
|
chm_file = None
|
||
|
try:
|
||
|
chm_file = RegisterHelpFile(True, lib_dir)
|
||
|
except Exception:
|
||
|
print("Failed to register help file")
|
||
|
traceback.print_exc()
|
||
|
else:
|
||
|
if verbose:
|
||
|
print("Registered help file")
|
||
|
|
||
|
# misc other fixups.
|
||
|
fixup_dbi()
|
||
|
|
||
|
# Register Pythonwin in context menu
|
||
|
try:
|
||
|
RegisterPythonwin(True, lib_dir)
|
||
|
except Exception:
|
||
|
print("Failed to register pythonwin as editor")
|
||
|
traceback.print_exc()
|
||
|
else:
|
||
|
if verbose:
|
||
|
print("Pythonwin has been registered in context menu")
|
||
|
|
||
|
# Create the win32com\gen_py directory.
|
||
|
make_dir = os.path.join(lib_dir, "win32com", "gen_py")
|
||
|
if not os.path.isdir(make_dir):
|
||
|
if verbose:
|
||
|
print("Creating directory %s" % (make_dir,))
|
||
|
directory_created(make_dir)
|
||
|
os.mkdir(make_dir)
|
||
|
|
||
|
try:
|
||
|
# create shortcuts
|
||
|
# CSIDL_COMMON_PROGRAMS only available works on NT/2000/XP, and
|
||
|
# will fail there if the user has no admin rights.
|
||
|
fldr = get_shortcuts_folder()
|
||
|
# If the group doesn't exist, then we don't make shortcuts - its
|
||
|
# possible that this isn't a "normal" install.
|
||
|
if os.path.isdir(fldr):
|
||
|
dst = os.path.join(fldr, "PythonWin.lnk")
|
||
|
create_shortcut(
|
||
|
os.path.join(lib_dir, "Pythonwin\\Pythonwin.exe"),
|
||
|
"The Pythonwin IDE",
|
||
|
dst,
|
||
|
"",
|
||
|
sys.prefix,
|
||
|
)
|
||
|
file_created(dst)
|
||
|
if verbose:
|
||
|
print("Shortcut for Pythonwin created")
|
||
|
# And the docs.
|
||
|
if chm_file:
|
||
|
dst = os.path.join(fldr, "Python for Windows Documentation.lnk")
|
||
|
doc = "Documentation for the PyWin32 extensions"
|
||
|
create_shortcut(chm_file, doc, dst)
|
||
|
file_created(dst)
|
||
|
if verbose:
|
||
|
print("Shortcut to documentation created")
|
||
|
else:
|
||
|
if verbose:
|
||
|
print("Can't install shortcuts - %r is not a folder" % (fldr,))
|
||
|
except Exception as details:
|
||
|
print(details)
|
||
|
|
||
|
# importing win32com.client ensures the gen_py dir created - not strictly
|
||
|
# necessary to do now, but this makes the installation "complete"
|
||
|
try:
|
||
|
import win32com.client # noqa
|
||
|
except ImportError:
|
||
|
# Don't let this error sound fatal
|
||
|
pass
|
||
|
print("The pywin32 extensions were successfully installed.")
|
||
|
|
||
|
if is_bdist_wininst:
|
||
|
# Open a web page with info about the .exe installers being deprecated.
|
||
|
import webbrowser
|
||
|
|
||
|
try:
|
||
|
webbrowser.open("https://mhammond.github.io/pywin32_installers.html")
|
||
|
except webbrowser.Error:
|
||
|
print("Please visit https://mhammond.github.io/pywin32_installers.html")
|
||
|
|
||
|
|
||
|
def uninstall(lib_dir):
|
||
|
# First ensure our system modules are loaded from pywin32_system, so
|
||
|
# we can remove the ones we copied...
|
||
|
LoadSystemModule(lib_dir, "pywintypes")
|
||
|
LoadSystemModule(lib_dir, "pythoncom")
|
||
|
|
||
|
try:
|
||
|
RegisterCOMObjects(False)
|
||
|
except Exception as why:
|
||
|
print("Failed to unregister COM objects: %s" % (why,))
|
||
|
|
||
|
try:
|
||
|
RegisterHelpFile(False, lib_dir)
|
||
|
except Exception as why:
|
||
|
print("Failed to unregister help file: %s" % (why,))
|
||
|
else:
|
||
|
if verbose:
|
||
|
print("Unregistered help file")
|
||
|
|
||
|
try:
|
||
|
RegisterPythonwin(False, lib_dir)
|
||
|
except Exception as why:
|
||
|
print("Failed to unregister Pythonwin: %s" % (why,))
|
||
|
else:
|
||
|
if verbose:
|
||
|
print("Unregistered Pythonwin")
|
||
|
|
||
|
try:
|
||
|
# remove gen_py directory.
|
||
|
gen_dir = os.path.join(lib_dir, "win32com", "gen_py")
|
||
|
if os.path.isdir(gen_dir):
|
||
|
shutil.rmtree(gen_dir)
|
||
|
if verbose:
|
||
|
print("Removed directory %s" % (gen_dir,))
|
||
|
|
||
|
# Remove pythonwin compiled "config" files.
|
||
|
pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin")
|
||
|
for fname in glob.glob(os.path.join(pywin_dir, "*.cfc")):
|
||
|
os.remove(fname)
|
||
|
|
||
|
# The dbi.pyd.old files we may have created.
|
||
|
try:
|
||
|
os.remove(os.path.join(lib_dir, "win32", "dbi.pyd.old"))
|
||
|
except os.error:
|
||
|
pass
|
||
|
try:
|
||
|
os.remove(os.path.join(lib_dir, "win32", "dbi_d.pyd.old"))
|
||
|
except os.error:
|
||
|
pass
|
||
|
|
||
|
except Exception as why:
|
||
|
print("Failed to remove misc files: %s" % (why,))
|
||
|
|
||
|
try:
|
||
|
fldr = get_shortcuts_folder()
|
||
|
for link in ("PythonWin.lnk", "Python for Windows Documentation.lnk"):
|
||
|
fqlink = os.path.join(fldr, link)
|
||
|
if os.path.isfile(fqlink):
|
||
|
os.remove(fqlink)
|
||
|
if verbose:
|
||
|
print("Removed %s" % (link,))
|
||
|
except Exception as why:
|
||
|
print("Failed to remove shortcuts: %s" % (why,))
|
||
|
# Now remove the system32 files.
|
||
|
files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*"))
|
||
|
# Try the system32 directory first - if that fails due to "access denied",
|
||
|
# it implies a non-admin user, and we use sys.prefix
|
||
|
try:
|
||
|
for dest_dir in [get_system_dir(), sys.prefix]:
|
||
|
# and copy some files over there
|
||
|
worked = 0
|
||
|
for fname in files:
|
||
|
base = os.path.basename(fname)
|
||
|
dst = os.path.join(dest_dir, base)
|
||
|
if os.path.isfile(dst):
|
||
|
try:
|
||
|
os.remove(dst)
|
||
|
worked = 1
|
||
|
if verbose:
|
||
|
print("Removed file %s" % (dst))
|
||
|
except Exception:
|
||
|
print("FAILED to remove %s" % (dst,))
|
||
|
if worked:
|
||
|
break
|
||
|
except Exception as why:
|
||
|
print("FAILED to remove system files: %s" % (why,))
|
||
|
|
||
|
|
||
|
# NOTE: If this script is run from inside the bdist_wininst created
|
||
|
# binary installer or uninstaller, the command line args are either
|
||
|
# '-install' or '-remove'.
|
||
|
|
||
|
# Important: From inside the binary installer this script MUST NOT
|
||
|
# call sys.exit() or raise SystemExit, otherwise not only this script
|
||
|
# but also the installer will terminate! (Is there a way to prevent
|
||
|
# this from the bdist_wininst C code?)
|
||
|
|
||
|
|
||
|
def verify_destination(location):
|
||
|
if not os.path.isdir(location):
|
||
|
raise argparse.ArgumentTypeError('Path "{}" does not exist!'.format(location))
|
||
|
return location
|
||
|
|
||
|
|
||
|
def main():
|
||
|
import argparse
|
||
|
|
||
|
parser = argparse.ArgumentParser(
|
||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
|
description="""A post-install script for the pywin32 extensions.
|
||
|
|
||
|
* Typical usage:
|
||
|
|
||
|
> python pywin32_postinstall.py -install
|
||
|
|
||
|
If you installed pywin32 via a .exe installer, this should be run
|
||
|
automatically after installation, but if it fails you can run it again.
|
||
|
|
||
|
If you installed pywin32 via PIP, you almost certainly need to run this to
|
||
|
setup the environment correctly.
|
||
|
|
||
|
Execute with script with a '-install' parameter, to ensure the environment
|
||
|
is setup correctly.
|
||
|
""",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-install",
|
||
|
default=False,
|
||
|
action="store_true",
|
||
|
help="Configure the Python environment correctly for pywin32.",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-remove",
|
||
|
default=False,
|
||
|
action="store_true",
|
||
|
help="Try and remove everything that was installed or copied.",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-wait",
|
||
|
type=int,
|
||
|
help="Wait for the specified process to terminate before starting.",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-silent",
|
||
|
default=False,
|
||
|
action="store_true",
|
||
|
help='Don\'t display the "Abort/Retry/Ignore" dialog for files in use.',
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-quiet",
|
||
|
default=False,
|
||
|
action="store_true",
|
||
|
help="Don't display progress messages.",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"-destination",
|
||
|
default=sysconfig.get_paths()["platlib"],
|
||
|
type=verify_destination,
|
||
|
help="Location of the PyWin32 installation",
|
||
|
)
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
if not args.quiet:
|
||
|
print("Parsed arguments are: {}".format(args))
|
||
|
|
||
|
if not args.install ^ args.remove:
|
||
|
parser.error("You need to either choose to -install or -remove!")
|
||
|
|
||
|
if args.wait is not None:
|
||
|
try:
|
||
|
os.waitpid(args.wait, 0)
|
||
|
except os.error:
|
||
|
# child already dead
|
||
|
pass
|
||
|
|
||
|
silent = args.silent
|
||
|
verbose = not args.quiet
|
||
|
|
||
|
if args.install:
|
||
|
install(args.destination)
|
||
|
|
||
|
if args.remove:
|
||
|
if not is_bdist_wininst:
|
||
|
uninstall(args.destination)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|