You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
5.7 KiB
140 lines
5.7 KiB
"""comtypes.client._code_cache helper module.
|
|
|
|
The main function is _find_gen_dir(), which on-demand creates the
|
|
comtypes.gen package and returns a directory where generated code can
|
|
be written to.
|
|
"""
|
|
import ctypes, logging, os, sys, tempfile, types
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _ensure_list(path):
|
|
"""
|
|
On Python 3.4 and later, when a package is imported from
|
|
an empty directory, its `__path__` will be a _NamespacePath
|
|
object and not a list, and _NamespacePath objects cannot
|
|
be indexed, leading to the error reported in #102.
|
|
This wrapper ensures that the path is a list for that reason.
|
|
"""
|
|
return list(path)
|
|
|
|
|
|
def _find_gen_dir():
|
|
"""Create, if needed, and return a directory where automatically
|
|
generated modules will be created.
|
|
|
|
Usually, this is the directory 'Lib/site-packages/comtypes/gen'.
|
|
|
|
If the above directory cannot be created, or if it is not a
|
|
directory in the file system (when comtypes is imported from a
|
|
zip-archive or a zipped egg), or if the current user cannot create
|
|
files in this directory, an additional directory is created and
|
|
appended to comtypes.gen.__path__ .
|
|
|
|
For a Python script using comtypes, the additional directory is
|
|
'%APPDATA%\<username>\Python\Python25\comtypes_cache'.
|
|
|
|
For an executable frozen with py2exe, the additional directory is
|
|
'%TEMP%\comtypes_cache\<imagebasename>-25'.
|
|
"""
|
|
_create_comtypes_gen_package()
|
|
from comtypes import gen
|
|
gen_path = _ensure_list(gen.__path__)
|
|
if not _is_writeable(gen_path):
|
|
# check type of executable image to determine a subdirectory
|
|
# where generated modules are placed.
|
|
ftype = getattr(sys, "frozen", None)
|
|
version_str = "%d%d" % sys.version_info[:2]
|
|
if ftype == None:
|
|
# Python script
|
|
subdir = r"Python\Python%s\comtypes_cache" % version_str
|
|
basedir = _get_appdata_dir()
|
|
|
|
elif ftype == "dll":
|
|
# dll created with py2exe
|
|
path = _get_module_filename(sys.frozendllhandle)
|
|
base = os.path.splitext(os.path.basename(path))[0]
|
|
subdir = r"comtypes_cache\%s-%s" % (base, version_str)
|
|
basedir = tempfile.gettempdir()
|
|
|
|
else: # ftype in ('windows_exe', 'console_exe')
|
|
# exe created by py2exe
|
|
base = os.path.splitext(os.path.basename(sys.executable))[0]
|
|
subdir = r"comtypes_cache\%s-%s" % (base, version_str)
|
|
basedir = tempfile.gettempdir()
|
|
|
|
gen_dir = os.path.join(basedir, subdir)
|
|
if not os.path.exists(gen_dir):
|
|
logger.info("Creating writeable comtypes cache directory: '%s'", gen_dir)
|
|
os.makedirs(gen_dir)
|
|
gen_path.append(gen_dir)
|
|
result = os.path.abspath(gen_path[-1])
|
|
logger.info("Using writeable comtypes cache directory: '%s'", result)
|
|
return result
|
|
|
|
################################################################
|
|
|
|
if os.name == "ce":
|
|
SHGetSpecialFolderPath = ctypes.OleDLL("coredll").SHGetSpecialFolderPath
|
|
GetModuleFileName = ctypes.WinDLL("coredll").GetModuleFileNameW
|
|
else:
|
|
SHGetSpecialFolderPath = ctypes.OleDLL("shell32.dll").SHGetSpecialFolderPathW
|
|
GetModuleFileName = ctypes.WinDLL("kernel32.dll").GetModuleFileNameW
|
|
SHGetSpecialFolderPath.argtypes = [ctypes.c_ulong, ctypes.c_wchar_p,
|
|
ctypes.c_int, ctypes.c_int]
|
|
GetModuleFileName.restype = ctypes.c_ulong
|
|
GetModuleFileName.argtypes = [ctypes.c_ulong, ctypes.c_wchar_p, ctypes.c_ulong]
|
|
|
|
CSIDL_APPDATA = 26
|
|
MAX_PATH = 260
|
|
|
|
def _create_comtypes_gen_package():
|
|
"""Import (creating it if needed) the comtypes.gen package."""
|
|
try:
|
|
import comtypes.gen
|
|
logger.info("Imported existing %s", comtypes.gen)
|
|
except ImportError:
|
|
import comtypes
|
|
logger.info("Could not import comtypes.gen, trying to create it.")
|
|
try:
|
|
comtypes_path = os.path.abspath(os.path.join(comtypes.__path__[0], "gen"))
|
|
if not os.path.isdir(comtypes_path):
|
|
os.mkdir(comtypes_path)
|
|
logger.info("Created comtypes.gen directory: '%s'", comtypes_path)
|
|
comtypes_init = os.path.join(comtypes_path, "__init__.py")
|
|
if not os.path.exists(comtypes_init):
|
|
logger.info("Writing __init__.py file: '%s'", comtypes_init)
|
|
ofi = open(comtypes_init, "w")
|
|
ofi.write("# comtypes.gen package, directory for generated files.\n")
|
|
ofi.close()
|
|
except (OSError, IOError) as details:
|
|
logger.info("Creating comtypes.gen package failed: %s", details)
|
|
module = sys.modules["comtypes.gen"] = types.ModuleType("comtypes.gen")
|
|
comtypes.gen = module
|
|
comtypes.gen.__path__ = []
|
|
logger.info("Created a memory-only package.")
|
|
|
|
def _is_writeable(path):
|
|
"""Check if the first part, if any, on path is a directory in
|
|
which we can create files."""
|
|
if not path:
|
|
return False
|
|
# TODO: should we add os.X_OK flag as well? It seems unnecessary on Windows.
|
|
return os.access(path[0], os.W_OK)
|
|
|
|
def _get_module_filename(hmodule):
|
|
"""Call the Windows GetModuleFileName function which determines
|
|
the path from a module handle."""
|
|
path = ctypes.create_unicode_buffer(MAX_PATH)
|
|
if GetModuleFileName(hmodule, path, MAX_PATH):
|
|
return path.value
|
|
raise ctypes.WinError()
|
|
|
|
def _get_appdata_dir():
|
|
"""Return the 'file system directory that serves as a common
|
|
repository for application-specific data' - CSIDL_APPDATA"""
|
|
path = ctypes.create_unicode_buffer(MAX_PATH)
|
|
# get u'C:\\Documents and Settings\\<username>\\Application Data'
|
|
SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, True)
|
|
return path.value
|