hack のためのネタ帳, etc,,,

状況

ようやく謎が解けた
pywin32 の公式リポジトリ には、
Installing via PIP
---------------------
You can install pywin32 via pip:
| pip install pywin32
Note that if you want to use pywin32 for "system wide" features, such as registering COM objects or implementing Windows Services, then you must run the following command from an elevated command prompt:
| python Scripts/pywin32_postinstall.py -install
みたいなことが書いてある。
"system wide" じゃなくてユーザ権限でインストールしたいので、それでは全然解決にならないからそれはやりたくない。

また、前回も書いた通り、このスクリプトユーザ権限で
CD %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37"
python Scripts/pywin32_postinstall.py -install
みたいにして実行しても
Parsed arguments are: Namespace(destination='C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\Lib\\site-packages', install=True, quiet=False, remove=False, silent=False, wait=None)
Traceback (most recent call last):
  File "Scripts/pywin32_postinstall.py", line 630, in <module>
    install(args.destination)
  File "Scripts/pywin32_postinstall.py", line 331, in install
    LoadSystemModule(lib_dir, "pywintypes")
  File "Scripts/pywin32_postinstall.py", line 171, in LoadSystemModule
    mod = importlib.util.module_from_spec(spec)
  File "<frozen importlib._bootstrap>", line 583, in module_from_spec
  File "<frozen importlib._bootstrap_external>", line 1043, in create_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
みたいにエラーになる。

上記は現時点のバージョン b300 で試した結果なのだが、具体的には以下の箇所。
  • pywin32 / blob / b300 / pywin32_postinstall.py: Line 171
これを print debug する目的で、以下のように 1 行追加してみた。
$ diff -c pywin32_postinstall{,2}.py
*** pywin32_postinstall.py      2020-11-17 16:04:01.024713700 +0900
--- pywin32_postinstall2.py     2020-11-17 19:56:01.093931600 +0900
***************
*** 168,173 ****
--- 168,174 ----
      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)
+     print("\n","="*40,"\n",spec,"\n","="*40,"\n")
      mod = importlib.util.module_from_spec(spec)
      spec.loader.exec_module(mod)

すると以下のようになった。
>python Scripts/pywin32_postinstall2.py -install
Parsed arguments are: Namespace(destination='C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\Lib\\site-packages', install=True, quiet=False, remove=False, silent=False, wait=None)

 ========================================
 ModuleSpec(name='pywintypes', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x00000284544848C8>, origin='C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\Lib\\site-packages\\pywin32_system32\\pywintypes37.dll')
 ========================================

Traceback (most recent call last):
  File "Scripts/pywin32_postinstall2.py", line 631, in <module>
    install(args.destination)
  File "Scripts/pywin32_postinstall2.py", line 332, in install
    LoadSystemModule(lib_dir, "pywintypes")
  File "Scripts/pywin32_postinstall2.py", line 172, in LoadSystemModule
    mod = importlib.util.module_from_spec(spec)
  File "<frozen importlib._bootstrap>", line 583, in module_from_spec
  File "<frozen importlib._bootstrap_external>", line 1043, in create_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
ImportError: DLL load failed: 指定されたモジュールが見つかりません。

pywintypes37.dll はユーザー環境に入っているが "system wide" には入ってないので、当然見つかるはずがない。
どうも、-destination オプションで %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\\site-packages" を与えてやる必要があるらしい。
すると以下のようになった。
>python Scripts/pywin32_postinstall2.py -install -destination %CD%\site-packages
Parsed arguments are: Namespace(destination='C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages', install=True, quiet=False, remove=False, silent=False, wait=None)

 ========================================
 ModuleSpec(name='pywintypes', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x0000020DE2044988>, origin='C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\pywin32_system32\\pywintypes37.dll')
 ========================================


 ========================================
 ModuleSpec(name='pythoncom', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0x0000020DE2068248>, origin='C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\pywin32_system32\\pythoncom37.dll')
 ========================================

Traceback (most recent call last):
  File "Scripts/pywin32_postinstall2.py", line 631, in <module>
    install(args.destination)
  File "Scripts/pywin32_postinstall2.py", line 380, in install
    "You don't have enough permissions to install the system files")
RuntimeError: You don't have enough permissions to install the system files

print debug 用に 171 行目に 1 行増やしたので、これは以下の箇所である。
  • pywin32 / blob / b300 / pywin32_postinstall.py: Line 379
これは、
  • pywin32 / blob / b300 / pywin32_postinstall.py: Line 340

get_system_dir() の結果 C:\WINDOWS\system32
sys.prefix の結果 C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0
を調べて、書き込み権限がないために失敗している。

ということで、一旦、最終目標は諦めて、公式サイトに書いてある通り、素直に管理者権限で cmd を起こして
CD %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37"
python Scripts/pywin32_postinstall.py -install -destination %CD%\site-packages
みたいにしてみると
Parsed arguments are: Namespace(destination='C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages', install=True, quiet=False, remove=False, silent=False, wait=None)
Copied pythoncom37.dll to C:\WINDOWS\system32\pythoncom37.dll
Copied pywintypes37.dll to C:\WINDOWS\system32\pywintypes37.dll
Registered: Python.Interpreter
Registered: Python.Dictionary
Registered: Python
-> Software\Python\PythonCore\3.7\Help[None]=None
-> Software\Python\PythonCore\3.7\Help\Pythonwin Reference[None]='C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\PyWin32.chm'
Pythonwin has been registered in context menu
Creating directory C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32com\gen_py
Can't install shortcuts - 'C:\\Users\\kou\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Python 3.7' is not a folder
The pywin32 extensions were successfully installed.
みたいになって、この状態であれば import win32api 可能になった。
ちゃんと、Microsoft Store 版の python でも import win32api 出来るじゃん!

しかし、そうじゃない。
"system wide" じゃなくユーザ権限のみで動かしたいので、全然解決になってない。
でもとりあえず、import win32api 出来ることは確認できたので、一歩前進。

上記の操作で "system wide" に設定されてしまったので、元に戻すため以下のようにしておいた。
CD %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37"
python Scripts/pywin32_postinstall.py -remove -destination %CD%\site-packages

さて、先程のスクリプトであるが、
  • pywin32 / blob / b300 / pywin32_postinstall.py: Line 331-333
を見てみると
LoadSystemModule(lib_dir, "pywintypes")
LoadSystemModule(lib_dir, "pythoncom")
import win32api
のような事をしていて、要するに "system wide" に設定せずとも
  • pythoncom37.dll
  • pywintypes37.dll
が読めてればちゃんと import win32api 出来るのである。

実際、
import site
from ctypes import *
PyDLL(site.USER_SITE+r"\pywin32_system32\pywintypes37.dll")
PyDLL(site.USER_SITE+r"\pywin32_system32\pythoncom37.dll")
import win32api
みたいにすると、きちんと import win32api できた。

つまり、
  • %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\win32api.pyd"
  • %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\pywin32_system32"
を検索してない事が原因ということが判明した。

さて、問題の win32api.pyd なのだが によれば、以下のファイルで構成されているらしい。 しかしぱっと見、
  • pythoncom37.dll
  • pywintypes37.dll
を LoadLibrary() してそうな箇所が見当たらない。

一方、 なんてファイルもあるのだが、これは
hasattr(sys, "frozen") が真なら
  • sys.path
以下
偽なら、
  • _win32sysloader.GetModuleFilename(filename)
  • _win32sysloader.LoadModule(filename)
  • os.path.join(sys.prefix, filename)
  • os.path.join(os.path.dirname(__file__), filename)
  • os.path.join(distutils.sysconfig.get_python_lib(plat_specific=1), "pywin32_system32", filename)
以下を順に検索するというコードが組まれている。

ところが、これも frozen、非 frozen 共に以下の有様。
>python
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 16:30:00) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pywintypes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\lib\pywintypes.py", line 105, in <module>
    __import_pywin32_system_module__("pywintypes", globals())
  File "C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\lib\pywintypes.py", line 87, in __import_pywin32_system_module__
    raise ImportError("No system module '%s' (%s)" % (modname, filename))
ImportError: No system module 'pywintypes' (pywintypes37.dll)
>python
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 16:30:00) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> setattr(sys,"frozen",True)
>>> import win32api
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
>>> import pywintypes
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\lib\pywintypes.py", line 105, in <module>
    __import_pywin32_system_module__("pywintypes", globals())
  File "C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\lib\pywintypes.py", line 38, in __import_pywin32_system_module__
    raise ImportError("Module '%s' isn't in frozen sys.path %s" % (modname, sys.path))
ImportError: Module 'pywintypes' isn't in frozen sys.path ['', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\python37.zip', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\DLLs', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\lib', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0', 'C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages', 'C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\win32', 'C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\win32\\lib', 'C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\Pythonwin', 'C:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\\lib\\site-packages']

sys.path を確認してみると
>python
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 16:30:00) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> for i in sys.path: print(i)
...

C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\python37.zip
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\DLLs
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\lib
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0
C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages
C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32
C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\win32\lib
C:\Users\kou\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\Pythonwin
C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.7_3.7.2544.0_x64__qbz5n2kfra8p0\lib\site-packages
みたいになってて、pywin32 の設定らしきパスがどこからか入り込んできてきちんと登録されているのに、なぜか、
  • %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\pywin32_system32"
が登録されてない。

これは、site モジュールの説明によると、site-package 以下の .pth ファイルの内容が反映されているらしい。
以下に、pywin32 が生成した .pth があり以下のような内容だった。

%USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\pywin32.pth"

# .pth file for the PyWin32 extensions
win32
win32\lib
Pythonwin

# And some hackery to deal with environments where the post_install script
# isn't run.
import pywin32_bootstrap
これに pywin32_system32 を追加して、以下のように変更。

%USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\pywin32.pth"

# .pth file for the PyWin32 extensions
win32
win32\lib
Pythonwin
pywin32_system32

# And some hackery to deal with environments where the post_install script
# isn't run.
import pywin32_bootstrap

結果、以下のようになった。
>python
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 16:30:00) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> setattr(sys,"frozen",True)
>>> import win32api
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: DLL load failed: 指定されたモジュールが見つかりません。
>>> import pywintypes
>>> import win32api
>>> win32api
<module 'win32api' from 'C:\\Users\\kou\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python37\\site-packages\\win32\\win32api.pyd'>
.pth に pywin32_system32 を追加した状態で frozen しておけば、import pywintypes 出来る。
それでも import win32api には失敗するが、import pywintypes が成功してれば、pythoncom37.dll と pywintypes37.dll の読み込みは完了しているので、 import win32api 可能になるという結果。
惜しい。

ということで、pythoncom37.dll と pywintypes37.dll を適当な関係してそうなフォルダにコピーして確認してみた所以下のような結果になった。
PATHwin32apipywintypes
site.USER_SITE××
site.USER_SITE+r"\pythonwin"××
site.USER_SITE+r"\win32"×
site.USER_SITE+r"\win32\lib"×
なお site.USER_SITE は今の場合
  • %USERPROFILE%"\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages
である。

統一感ねぇな、何だよこれ!?
って感じだが、とりあえず、上記の場所に、pythoncom37.dll と pywintypes37.dll を置いておけば Microsoft Store 版の Python でも、とりあえず import win32api が出来ることが分かった。

さて、本家に報告したいのだが、英語、面倒臭い。
今日は流石に力尽きたので、後日余力がある時に頑張って報告かなぁ。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます