NB: I know the proper solution is just to do pip install pywin32
and let everything be done automatically, but here this question is about the internals of pythoncom
.
When doing import pythoncom
, it works, but:
in "C:\Python38\Lib\site-packages\pythoncom.py", there is import pywintypes
, but no pywintypes.py or .pyd or .dll is present in the site-packages
directory. How can it "magically" find pywintypes
?
when doing print(pythoncom.__file__)
, we see:
'C:\\Python38\\lib\\site-packages\\pywin32_system32\\pythoncom38.dll'
How is this stunning behaviour possible internally? (i.e. pythoncom.py that we imported is now recognized as another file, a .dll)
Also, pythoncom.py
contains:
# Magic utility that "redirects" to pythoncomxx.dll
import pywintypes
pywintypes.__import_pywin32_system_module__("pythoncom", globals())
What and where is this (I quote) "magic utility" that redirects to pythoncomxx.dll
?
I don't see where this utility is called when doing just import pythoncom
.
I believe the magic utility is pywintypes.__import_pywin32_system_module__
combined with _win32sysloader
.
The DLL path is built here (where modname is 'pythoncom'):
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,
)
which gets passed to _win32sysloader:
found = _win32sysloader.LoadModule(filename)
which is a C++ file that loads the DLL:
HINSTANCE hinst = LoadLibraryEx(modName, NULL,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS |
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
The final result is the path of the loaded DLL, which gets registered as a Python module here:
# Load the DLL.
loader = importlib.machinery.ExtensionFileLoader(modname, found)
spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=found)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
As for why pywintypes, it should be under Lib/site-packages/win32/lib/pywintypes.py
. But if you actually import it you get another DLL path:
>>> import pywintypes
>>> sys.modules['pywintypes']
<module 'pywintypes' (C:\Python312\Lib\site-packages\pywin32_system32\pywintypes312.dll)>
But that's because pywintypes
uses its own __import_pywin32_system_module__
function to replace itself:
__import_pywin32_system_module__("pywintypes", globals())
And finally, the reason that importing pywintypes
from Lib\site-packages\win32\lib
works is due to the library's pywin32.pth
file (per @Jeronimo's comment), a path configuration file. This file is automatically imported at interpreter start, and the paths there are added to the import paths.