"""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%\\Python\Python25\comtypes_cache'. For an executable frozen with py2exe, the additional directory is '%TEMP%\comtypes_cache\-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\\\\Application Data' SHGetSpecialFolderPath(0, path, CSIDL_APPDATA, True) return path.value