PythonService를 사용합니다.가상 환경을 사용하는 동안 python 서비스를 호스팅하기 위해 exe
나는 Python 3.4를 사용하는 Python Windows 서비스를 개발해야 하는 Windows 7 환경을 가지고 있다. 나는 pywin32의 win32 서비스 모듈을 사용하여 서비스를 설정하고 있으며 대부분의 후크가 정상적으로 작동하는 것 같습니다.
문제는 소스 코드에서 서비스를 실행하려고 할 때입니다. 이는 PythonService를 사용합니다.exe to host service.py - 하지만 venv 가상 환경을 사용하고 있는데 스크립트에서 해당 모듈을 찾을 수 없습니다(에서 오류 메시지가 발견됨).
Pywin32는 가상 환경에 설치되어 PythonService의 소스 코드를 조사합니다.exe는 Python34.dll에서 동적으로 링크되며, 내 service.py 을 가져와서 호출한다.
파이썬 서비스를 받으려면 어떻게 해야 합니까.service.py 을 실행할 때 가상 환경을 사용하려면 exe?
가상 환경이 Python 3.3에 추가되기 전에 모듈과 올바르게 작동하는 데 사용된 것으로 보입니다. Python이 가져오기를 충족하는 디렉터리를 찾을 때까지 실행 파일에서 위쪽을 바라보곤 했다는 일화적인 증거가 있습니다(이 답변 참조). 그런 다음 PythonService에 이를 사용하면 충분했습니다.exe를 사용하여 내부에 있던 가상 환경을 찾아 사용합니다.
만약 그것이 행동이었다면, 모듈의 도입으로 더 이상 그렇게 하지 않는 것으로 보인다. 대신 파일에 대해 한 단계 높은 수준을 확인하고 가상 환경에 대해서만 구성합니다. 물론 이것은 PythonService에서는 작동하지 않습니다.exe는 사이트 분석 아래 Pywin32 모듈에 묻혀 있습니다.
이 문제를 해결하기 위해 원래 모듈과 함께 제공되는 코드를 수정했습니다(다음 답변 참조). PythonService.exe의 경우와 같이 실행 파일에 포함된 인터프리터를 가상 환경을 사용하도록 부트스트랩하는 데 사용됩니다. 유감스럽게도 에서는 이 항목을 포함하지 않습니다.
제게 효과가 있었던 것은 이렇습니다. 여기에서는 가상 환경의 이름이 my-venv이며 소스 코드 위치보다 한 단계 위에 위치한다고 가정합니다.
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
내 문제의 또 다른 요인은 Pip 설치를 더 쉽게 하는 Twisted pops에 의해 제공되는 Pywin32용 새로운 Pypi 휠이다. 파이썬 서비스입니다.easy_install을 사용하여 가상 환경에 공식 win32 exe 패키지를 설치할 때 나타나는 것과 비교하여 해당 패키지의 exe가 이상하게 동작하고 있었습니다(실행 시 pywin32 dll을 찾을 수 없습니다.
이 질문과 해결책을 올려주셔서 정말 감사합니다. 나는 또한 유용할 수도 있는 약간 다른 접근법을 취했다. 가상 환경에서 Python 서비스에 대한 작업 팁을 찾는 것은 물론이고 가상 환경에서 작업하는 것도 매우 어렵습니다. 어쨌든...
스텝
이는 Windows 7 x64, Python 3.5.1 x64, Pywin32-220(또는 pypiwin32-219)을 사용하는 것입니다.
- 관리자 명령 프롬프트를 엽니다.
- 가상 환경을 생성합니다.
- 가상 환경을 활성화합니다.
- Pywin32를 설치합니다:
- 설치 후 스크립트를 실행합니다.
- 복사 대상
논의
이것이 작동하는 이유는 Python이 Libs 폴더가 어디에 있는지 파악하기 위해 위쪽을 보고 그것을 기반으로 승인된 답변과 유사하게 패키지 가져오기 경로를 설정하기 때문이라고 생각합니다. 파이썬 서비스일 때.exe가 원래 위치에 있는데, 원활하게 작동하지 않는 것 같습니다.
또한 DLL 연결 문제를 해결합니다(에서 depends.exe로 검색 가능). DLL 비즈니스가 정리되지 않으면 스크립트의 모듈로 *.pyd 파일을 가져올 수 없습니다. 예를 들어 패키지에 .py 파일로 포함되어 있지 않고 윈도우즈 이벤트 로그 기능이 몇 가지 있으므로 allow가 필요합니다.
제가 수용된 답변에서 겪었던 문제 중 하나는 사용할 때 생성되는 package.eg-link 경로를 정확하게 픽업하는 방법을 찾을 수 없다는 것입니다. 이러한 .egg-link 파일에는 패키지가 아래의 가상 환경에 없는 경우 패키지의 경로가 포함됩니다.
모든 것이 순조롭게 진행되면 (활성화된 가상 환경의 관리 프롬프트에서) win32 서비스 예제를 설치, 시작 및 테스트할 수 있어야 합니다:
python venv\Lib\site-packages\win32\Demos\service\pipeTestService.py install
python venv\Lib\site-packages\win32\Demos\service\pipeTestService.py start
python venv\Lib\site-packages\win32\Demos\service\pipeTestServiceClient.py
서비스 환경
이 모든 것에서 또 다른 중요한 참고 사항은 서비스가 당신이 실행할 수 있는 환경과 완전히 다른 환경에서 파이썬 코드를 실행할 것이라는 것이다. 예를 들어 서비스를 실행할 때 빈 공간이 될 것이다. 이 작업은 다음 중 하나를 통해 처리할 수 있습니다:
- 스크립트 내부에서 환경 변수 설정(예.
- 환경 변수가 있는 서비스에 레지스트리 키를 추가합니다.
2018년에 읽는 사람들에게, 나는 위의 두 솔루션(윈10, 파이썬 3.6) 중 어느 것도 운이 없었다 - 그래서 이것이 내가 그것을 작동시키기 위해 한 것이다. 작업 디렉터리는 시작 시 site-packages/win32에 있으므로 프로젝트 코드를 가져오려면 먼저 작업 디렉터리를 변경하고 sys.path를 수정해야 합니다. 이 venv는 프로젝트 dir에 있으며, 그렇지 않으면 일부 경로만 하드 코딩하면 됩니다:
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):
[...]
나는 모든 답을 읽었지만, 어떤 해결책도 내 문제를 해결할 수 없다.
의 코드를 주의 깊게 조사한 후, 나는 약간의 변화를 가했고, 마침내 그것이 작동했다.
하지만 제 명성이 충분하지 않아서 그냥 여기에 코드를 올립니다.
# 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
어떻게 사용하나요? 단순하기 때문에 새 파이썬 파일에 붙여넣고 다음과 같은 서드파티 모델 앞에 가져오기만 하면 됩니다:
import service_in_venv # import at top
import win32serviceutil
import win32service
import win32event
import servicemanager
import time
import sys, os
........
그리고 이제 당신은 당신의 문제를 고쳐야 합니다.
"pythonservice.exe"를 사용하지 말고 서비스에 직접 등록하십시오:
import win32serviceutil
import win32service
import servicemanager
import sys
import os
import os.path
import multiprocessing
#
def main():
import time
time.sleep(600)
class ProcessService(win32serviceutil.ServiceFramework):
_svc_name_ = "SleepService"
_svc_display_name_ = "Sleep Service"
_svc_description_ = "Sleeps for 600"
_exe_name_ = sys.executable # python.exe from venv
_exe_args_ = '-u -E "' + os.path.abspath(__file__) + '"'
proc = None
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
if self.proc:
self.proc.terminate()
def SvcRun(self):
self.proc = multiprocessing.Process(target=main)
self.proc.start()
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.SvcDoRun()
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
def SvcDoRun(self):
self.proc.join()
def start():
if len(sys.argv)==1:
import win32traceutil
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(ProcessService)
servicemanager.StartServiceCtrlDispatcher()
elif '--fg' in sys.argv:
main()
else:
win32serviceutil.HandleCommandLine(ProcessService)
if __name__ == '__main__':
try:
start()
except (SystemExit, KeyboardInterrupt):
raise
except:
import traceback
traceback.print_exc()
서비스 설치와 함께 오른쪽 인터프리터를 가리키며 파이썬 3.5+ 가상 환경을 지원한다.
나는 최근에 임베디드 인터프리터로 이 문제를 해결했고 임베디드 인터프리터와 일반 가상 환경 모두에서 파이썬 3.11에 작동하는 이러한 지침을 고안했다.
이 솔루션은 의 공유 DLL에 의존하지 않는다는 점에서 현재 가장 많이 사용되는 답변과 다릅니다.
마지막으로 필요한 환경 설정을 보여주는 파이썬 스크립트가 제공된다.
가상 환경
PS D:\dev\python_winsvc> C:\Python\Python311\python.exe -m venv venv_311
PS D:\dev\python_winsvc> . .\venv_311\Scripts\activate
(venv_311) PS D:\dev\python_winsvc> pip install pywin32
Collecting pywin32
Using cached pywin32-306-cp311-cp311-win_amd64.whl (9.2 MB)
Installing collected packages: pywin32
Successfully installed pywin32-306
(venv_311) PS D:\dev\python_winsvc> deactivate
다음과 같이 표시:
python.exe # already there
python3.dll # copy/link from venv source interpreter
python311.dll # copy/link from venv source interpreter
pythoncom311.dll # copy/link from .\venv_311\Lib\site-packages\pywin32_system32
pywintypes311.dll # copy/link from .\venv_311\Lib\site-packages\pywin32_system32
pythonservice.exe # copy/link from .\venv_311\Lib\site-packages\win32
servicemanager.pyd # copy/link from .\venv_311\Lib\site-packages\win32
...
서비스 설치를 수행합니다. 그렇지 않으면 잘못된 비단뱀 서비스.exe는 서비스 실행 파일로 사용되며 필요한 DLL의 폴더와 동일한 폴더에 있어야 하므로 에 있어야 합니다.
PS D:\dev\python_winsvc> .\venv_311\Scripts\python .\winsvc.py install
Installing service python-winsvc
Service installed
PS D:\dev\python_winsvc> .\venv_311\Scripts\python .\winsvc.py start
Starting service python-winsvc
임베디드 인터프리터
임베디드 인터프리터에 pip 모듈이 부족하기 때문에 우리는 정기적으로 설치된 것을 사용하여 임베디드 인터프리터를 위한 pip 패키지를 준비한다.
PS D:\dev\python_winsvc> C:\Python\Python311\python.exe -m pip install --target embed_311\lib\site-packages pywin32
Collecting pywin32
Using cached pywin32-306-cp311-cp311-win_amd64.whl (9.2 MB)
Installing collected packages: pywin32
Successfully installed pywin32-306
다음과 같이 표시:
python.exe # already there
python3.dll # already there
python311.dll # already there
pythoncom311.dll # copy/link from .\embed_311\Lib\site-packages\pywin32_system32
pywintypes311.dll # copy/link from .\embed_311\Lib\site-packages\pywin32_system32
pythonservice.exe # copy/link from .\embed_311\Lib\site-packages\win32
servicemanager.pyd # copy/link from .\embed_311\Lib\site-packages\win32
...
PS D:\dev\python_winsvc> .\embed_311\python.exe .\winsvc.py install
Installing service python-winsvc
Service installed
PS D:\dev\python_winsvc> .\embed_311\python.exe .\winsvc.py start
Starting service python-winsvc
서비스 예제
# winsvc.py
import sys
import pathlib
PYTHON_PATH = pathlib.Path(sys.executable).parent
import site
site.addsitedir(PYTHON_PATH.joinpath("lib/site-packages")) # Only required when using the embedded interpreter
from typing import *
import logging, logging.handlers
import threading
import time
import win32event
import win32evtlogutil
import win32service
import win32serviceutil
import servicemanager
def configure_logger(filename: str) -> logging.Logger:
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s %(levelname)5.5s: %(message)s")
handlers = [
logging.handlers.RotatingFileHandler(pathlib.Path(__file__).parent.joinpath(filename), maxBytes=1024*1024, backupCount=0),
logging.StreamHandler()
]
for handler in handlers:
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
logger = configure_logger("winsvc.log")
class ApplicationThread(threading.Thread):
def __init__(self) -> None:
super().__init__()
self._exit = False
def stop(self) -> None:
self._exit = True
def run(self) -> None:
logger.debug("service is running")
while not self._exit:
time.sleep(1)
class Win32ServiceWrapper(win32serviceutil.ServiceFramework):
_exe_name_ = str(PYTHON_PATH.joinpath("pythonservice.exe"))
_svc_name_ = "python-winsvc"
_svc_display_name_ = "Python WinSvc"
def __init__(self, args: Iterable[str]) -> None:
super().__init__(args)
self._stop_event = win32event.CreateEvent(None, 0, 0, None)
self._thread = ApplicationThread()
def SvcStop(self) -> None:
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self._stop_event)
def SvcDoRun(self):
win32evtlogutil.ReportEvent(self._svc_display_name_, servicemanager.PYS_SERVICE_STARTED, 0, servicemanager.EVENTLOG_INFORMATION_TYPE, (self._svc_name_, ''))
self._thread.start()
win32event.WaitForSingleObject(self._stop_event, win32event.INFINITE)
self._thread.stop()
self._thread.join()
win32evtlogutil.ReportEvent(self._svc_display_name_, servicemanager.PYS_SERVICE_STOPPED, 0, servicemanager.EVENTLOG_INFORMATION_TYPE, (self._svc_name_, ''))
if __name__ == "__main__":
win32serviceutil.HandleCommandLine(Win32ServiceWrapper)
'개발하자' 카테고리의 다른 글
| Svelte 각 작업이 두 번 / 각 바인딩 오류(정의되지 않은 속성을 설정할 수 없음) (1) | 2023.05.17 |
|---|---|
| Python: 이메일을 보낼 때 다른 우편함 사용 (0) | 2023.05.16 |
| Terraform - 웹 서버에 연결할 수 없음 - 인스턴스가 서비스를 종료했습니다 (0) | 2023.05.15 |
| 벨벳 키트에서 마크다운 파일을 가져오는 방법은 무엇입니까? (0) | 2023.05.15 |
| 왜 쿠버네티스에 있는 포드들이 서비스 계정 비밀을 자동으로 탑재하고 있나요? (1) | 2023.05.14 |