123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- # postinstall script for pywin32
- #
- # copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory,
- # and creates a pth file
- import os
- import sys
- import glob
- import shutil
- import time
- import distutils.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 as details:
- # 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, shellcon
- 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):
- import pythoncom
- 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, 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
- tb = None
- 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.util, importlib.machinery
- 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)
- finally:
- my_key.Close()
- finally:
- root_key.Close()
- if verbose:
- print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value))
- def RegisterCOMObjects(register = 1):
- 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 RegisterPythonwin(register=True):
- """ 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
- lib_dir = distutils.sysconfig.get_python_lib(plat_specific=1)
- 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:
- 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, 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
- # If this isn't sys.prefix (ie, System32), then nuke
- # any versions that may exist in sys.prefix - having
- # duplicates causes major headaches.
- if dest_dir != sys.prefix:
- bad_fname = os.path.join(sys.prefix, 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:
- 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)
- # 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)
- else:
- print("NOTE: PyWin32.chm can not be located, so has not " \
- "been registered")
- # misc other fixups.
- fixup_dbi()
- # Register Pythonwin in context menu
- try:
- RegisterPythonwin()
- except:
- 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.
- 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
- except ImportError:
- # Don't let this error sound fatal
- pass
- print("The pywin32 extensions were successfully installed.")
- 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:
- RegisterPythonwin(False)
- 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
- if __name__ == '__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=distutils.sysconfig.get_python_lib(plat_specific=1),
- 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 AttributeError:
- # Python 2.2 - no waitpid - just sleep.
- time.sleep(3)
- 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)
|