Python Windows Service using Python3.4。pywin32のwin32serviceモジュールを使用してサービスをセットアップする必要があるWindows7環境がありますほとんどのフックは正常に機能しているようです。
問題は、ソースコードからサービスを実行しようとしたときです(python service.py install
の後にpython service.py start
を使用)。これはPythonService.exeを使用してservice.pyをホストします-しかし、私はvenv仮想環境を使用しており、スクリプトはそのモジュールを見つけることができません(エラーメッセージはpython service.py debug
で発見されました)。
Pywin32はvirtualenvにインストールされ、PythonService.exeのソースコードを見ると、Python34.dllに動的にリンクし、service.pyをインポートして呼び出します。
Service.pyの実行時にPythonService.exeでvirtualenvを使用するにはどうすればよいですか?
仮想環境がPython 3.3に追加される前は、これはvirtualenv
モジュールで正しく機能していたようです。事例証拠があります: https:// stackoverflow.com/a/12424980/1055722 )Pythonのsite.py
は、インポートを満たすディレクトリが見つかるまで実行可能ファイルから上向きに検索していました。その後、それをsys.prefix
とこれは、PythonService.exeが内部にあるvirtualenvを見つけて使用するのに十分でした。
それが動作だった場合、site.py
はvenv
モジュールの導入によりもはやそれを行わないようです。代わりに、pyvenv.cfg
ファイルを1レベル上で検索し、その場合にのみ仮想環境用に構成します。もちろん、これは、site-packagesの下のpywin32モジュールに埋め込まれているPythonService.exeでは機能しません。
それを回避するために、元のvirtualenv
モジュールに付属するactivate_this.py
コードを適応させました(この回答を参照してください: https://stackoverflow.com/a/33637378/1055722 =)。これは、実行可能ファイル(PythonService.exeの場合)に埋め込まれたインタープリターがvirtualenvを使用するためにbootstrapインタープリター)に使用されます。残念ながら、venv
にはこれが含まれていません。
これが私のために働いたものです。これは、仮想環境の名前がmy-venvであり、ソースコードの場所の1レベル上にあることを前提としていることに注意してください。
import os
import sys
if sys.executable.endswith("PythonService.exe"):
# Change current working directory from PythonService.exe location to something better.
service_directory = os.path.dirname(__file__)
source_directory = os.path.abspath(os.path.join(service_directory, ".."))
os.chdir(source_directory)
sys.path.append(".")
# Adapted from virtualenv's activate_this.py
# Manually activate a virtual environment inside an already initialized interpreter.
old_os_path = os.environ['PATH']
venv_base = os.path.abspath(os.path.join(source_directory, "..", "my-venv"))
os.environ['PATH'] = os.path.join(venv_base, "Scripts") + os.pathsep + old_os_path
site_packages = os.path.join(venv_base, 'Lib', 'site-packages')
prev_sys_path = list(sys.path)
import site
site.addsitedir(site_packages)
sys.real_prefix = sys.prefix
sys.prefix = venv_base
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
私の悩みのもう1つの要因は、pywin32用の新しいpypiホイールがあります。これは、pipを使用して簡単にインストールできるTwistedの人々によって提供されています。そのパッケージのPythonService.exeは、easy_installを使用して公式のwin32 exeパッケージを仮想環境にインストールしたときに得られるものと比較して、奇妙な動作をしていました(呼び出されたときにpywin32 dllを見つけることができませんでした)。
私はすべての答えを読みましたが、解決策で問題を解決することはできません。
慎重に調査した後 David K. Hess のコードを変更したところ、ようやく機能しました。
しかし、私の評判は十分ではないので、ここにコードを投稿するだけです。
# 1. Custom your Project's name and Virtual Environment folder's name
# 2. Import this before all third part models
# 3. If you still failed, check the link below:
# https://stackoverflow.com/questions/34696815/using-pythonservice-exe-to-Host-python-service-while-using-virtualenv
# 2019-05-29 by oraant, modified from David K. Hess's answer.
import os, sys, site
project_name = "PythonService" # Change this for your own project !!!!!!!!!!!!!!
venv_folder_name = "venv" # Change this for your own venv path !!!!!!!!!!!!!!
if sys.executable.lower().endswith("pythonservice.exe"):
# Get root path for the project
service_directory = os.path.abspath(os.path.dirname(__file__))
project_directory = service_directory[:service_directory.find(project_name)+len(project_name)]
# Get venv path for the project
def file_path(x): return os.path.join(project_directory, x)
venv_base = file_path(venv_folder_name)
venv_scripts = os.path.join(venv_base, "Scripts")
venv_packages = os.path.join(venv_base, 'Lib', 'site-packages')
# Change current working directory from PythonService.exe location to something better.
os.chdir(project_directory)
sys.path.append(".")
prev_sys_path = list(sys.path)
# Manually activate a virtual environment inside an already initialized interpreter.
os.environ['PATH'] = venv_scripts + os.pathsep + os.environ['PATH']
site.addsitedir(venv_packages)
sys.real_prefix = sys.prefix
sys.prefix = venv_base
# Move some sys path in front of others
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
それを使用する方法?簡単です。新しいpythonファイルに貼り付けて、次のようなサードパーティモデルの前にインポートするだけです。
import service_in_venv # import at top
import win32serviceutil
import win32service
import win32event
import servicemanager
import time
import sys, os
........
そして今、あなたはあなたの問題を修正する必要があります。
2018年に読んだ人にとって、上記のどちらのソリューションでも運がなかった(Win10、Python 3.6)-これが私がそれを機能させるためにしたことです。作業ディレクトリはサイトにあります-起動時にpackages/win32であるため、プロジェクトコードをインポートする前に、作業ディレクトリを変更してsys.pathを修正する必要があります。この想定されるvenvはプロジェクトディレクトリにあります。そうでない場合は、いくつかのパスをハードコーディングする必要があります。
import sys
import os
if sys.executable.lower().endswith("pythonservice.exe"):
for i in range(4): # goes up 4 directories to project folder
os.chdir("..")
# insert site-packages 2nd in path (behind project folder)
sys.path.insert(1, os.path.join("venv",'Lib','site-packages'))
[REST OF IMPORTS]
class TestService(win32serviceutil.ServiceFramework):
[...]